diff --git a/Xamarin.Forms.ControlGallery.Android/CustomRenderers.cs b/Xamarin.Forms.ControlGallery.Android/CustomRenderers.cs index e6d70b96abf..e94afd93874 100644 --- a/Xamarin.Forms.ControlGallery.Android/CustomRenderers.cs +++ b/Xamarin.Forms.ControlGallery.Android/CustomRenderers.cs @@ -115,7 +115,7 @@ protected override void UpdateMenuItemIcon(Context context, IMenuItem menuItem, if (toolBarItem.IconImageSource is FileImageSource fileImageSource) { var name = IOPath.GetFileNameWithoutExtension(fileImageSource.File); - var id = Xamarin.Forms.Platform.Android.ResourceManager.GetDrawableByName(name); + var id = context.GetDrawableId(name); if (id != 0) { if ((int)Build.VERSION.SdkInt >= 21) diff --git a/Xamarin.Forms.ControlGallery.Android/FormsAppCompatActivity.cs b/Xamarin.Forms.ControlGallery.Android/FormsAppCompatActivity.cs index d239b290489..7c910891cd3 100644 --- a/Xamarin.Forms.ControlGallery.Android/FormsAppCompatActivity.cs +++ b/Xamarin.Forms.ControlGallery.Android/FormsAppCompatActivity.cs @@ -51,6 +51,12 @@ protected override void OnCreate(Bundle bundle) #endif Forms.Init(this, bundle); + // null out the assembly on the Resource Manager + // so all of our tests run without using the reflection APIs + // At some point the Resources class types will go away so + // reflection will stop working + ResourceManager.Init(null); + FormsMaps.Init(this, bundle); FormsMaterial.Init(this, bundle); AndroidAppLinks.Init(this); diff --git a/Xamarin.Forms.Platform.Android.UnitTests/ButtonTests.cs b/Xamarin.Forms.Platform.Android.UnitTests/ButtonTests.cs index e56172ef76a..8e127fc9749 100644 --- a/Xamarin.Forms.Platform.Android.UnitTests/ButtonTests.cs +++ b/Xamarin.Forms.Platform.Android.UnitTests/ButtonTests.cs @@ -12,6 +12,7 @@ using Xamarin.Forms; using Xamarin.Forms.CustomAttributes; using AColor = Android.Graphics.Color; +using AContextThemeWrapper = Android.Support.V7.View.ContextThemeWrapper; namespace Xamarin.Forms.Platform.Android.UnitTests { @@ -95,25 +96,26 @@ public void StyleTextAllCapsSettingIsRespected() } // This is the ideal test for Issue11703. It's currently being tabled due to a Resource linking bug that we're working out with Android team - //[Category("Button")] - //[Description("Account for user's setting of styles property textAllCaps")] - //[Issue(IssueTracker.Github, 11703, "[Bug] Android textAllCaps no longer works", issueTestNumber: 1)] - //[TestCase(false)] - //[TestCase(true)] - //public void StyleTextAllCapsSettingIsRespected(bool allCaps) - //{ - // ContextThemeWrapper contextThemeWrapper = null; - // if (allCaps) - // contextThemeWrapper = new ContextThemeWrapper(Context, Resource.Style.TextAllCapsStyleTrue); - // else - // contextThemeWrapper = new ContextThemeWrapper(Context, Resource.Style.TextAllCapsStyleFalse); + /*[Category("Button")] + [Description("Account for user's setting of styles property textAllCaps")] + [Issue(IssueTracker.Github, 11703, "[Bug] Android textAllCaps no longer works", issueTestNumber: 1)] + [TestCase(false)] + [TestCase(true)] + public async Task StyleTextAllCapsSettingIsRespected(bool allCaps) + { + AContextThemeWrapper contextThemeWrapper = null; + if (allCaps) + contextThemeWrapper = new AContextThemeWrapper(Context, Context.GetStyle("TextAllCapsStyleTrue")); + else + contextThemeWrapper = new AContextThemeWrapper(Context, Context.GetStyle("TextAllCapsStyleFalse")); - // var button = new Button { Text = "foo" }; - // var buttonControl = GetRenderer(button, contextThemeWrapper).View as AppCompatButton; - // var initialTextTransform = buttonControl.TransformationMethod; + var button = new Button { Text = "foo" }; + var initialTextTransform = await GetControlProperty(button, x => x.TransformationMethod); - // Assert.AreEqual(allCaps, initialTextTransform is AllCapsTransformationMethod); - //} + // when set through a style the type is an internal version of AllCapsTransformationMethod + string typeName = $"{initialTextTransform}"; + Assert.AreEqual(allCaps, typeName.Contains("AllCapsTransformationMethod")); + }*/ } } diff --git a/Xamarin.Forms.Platform.Android.UnitTests/ResourceManagerTests.cs b/Xamarin.Forms.Platform.Android.UnitTests/ResourceManagerTests.cs new file mode 100644 index 00000000000..c2d457985f4 --- /dev/null +++ b/Xamarin.Forms.Platform.Android.UnitTests/ResourceManagerTests.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using Android.Views; +using NUnit.Framework; + +namespace Xamarin.Forms.Platform.Android.UnitTests +{ + [TestFixture] + public class ResourceManagerTests : PlatformTestFixture + { + [Test, Category("Resource")] + [Description("Retrieve Resources by Name")] + public void RetrieveResourcesByName() + { + ResourceManager.Init(null); + ResourceManager.DrawableClass = null; + ResourceManager.LayoutClass = null; + ResourceManager.ResourceClass = null; + ResourceManager.StyleClass = null; + + Assert.Greater(ResourceManager.GetDrawableId(this.Context, "DrawableTEST"), 0); + Assert.Greater(ResourceManager.GetDrawableId(this.Context, "DrawableTEST.png"), 0); + Assert.Greater(ResourceManager.GetLayout(this.Context, "LayoutTest"), 0); + Assert.Greater(ResourceManager.GetStyle(this.Context, "TextAllCapsStyleTrue"), 0); + Assert.Greater(ResourceManager.GetResource(this.Context, "namewith.adot"), 0); + Assert.Greater(ResourceManager.GetResource(this.Context, "namewith_adot"), 0); + + } + } +} diff --git a/Xamarin.Forms.Platform.Android.UnitTests/Resources/drawable/DrawableTest.png b/Xamarin.Forms.Platform.Android.UnitTests/Resources/drawable/DrawableTest.png new file mode 100644 index 00000000000..fbf2947e12a Binary files /dev/null and b/Xamarin.Forms.Platform.Android.UnitTests/Resources/drawable/DrawableTest.png differ diff --git a/Xamarin.Forms.Platform.Android.UnitTests/Resources/layout/LayoutTest.axml b/Xamarin.Forms.Platform.Android.UnitTests/Resources/layout/LayoutTest.axml new file mode 100644 index 00000000000..cc009618c21 --- /dev/null +++ b/Xamarin.Forms.Platform.Android.UnitTests/Resources/layout/LayoutTest.axml @@ -0,0 +1,23 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android.UnitTests/Xamarin.Forms.Platform.Android.UnitTests.csproj b/Xamarin.Forms.Platform.Android.UnitTests/Xamarin.Forms.Platform.Android.UnitTests.csproj index bac8d468ac5..8daa803ef28 100644 --- a/Xamarin.Forms.Platform.Android.UnitTests/Xamarin.Forms.Platform.Android.UnitTests.csproj +++ b/Xamarin.Forms.Platform.Android.UnitTests/Xamarin.Forms.Platform.Android.UnitTests.csproj @@ -70,6 +70,7 @@ + @@ -97,6 +98,12 @@ + + + + + + \ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/Platform.cs b/Xamarin.Forms.Platform.Android/Platform.cs index e4dc4448fdd..984eb84c6ab 100644 --- a/Xamarin.Forms.Platform.Android/Platform.cs +++ b/Xamarin.Forms.Platform.Android/Platform.cs @@ -803,7 +803,7 @@ void GetNewMasterDetailToggle() if (fileImageSource == null) throw new InvalidOperationException("Icon property must be a FileImageSource on Master page"); - int icon = ResourceManager.GetDrawableByName(fileImageSource); + int icon = _activity.GetDrawableId(fileImageSource); FastRenderers.AutomationPropertiesProvider.GetDrawerAccessibilityResources(_activity, CurrentMasterDetailPage, out int resourceIdOpen, out int resourceIdClose); #pragma warning disable 618 // Eventually we will need to determine how to handle the v7 ActionBarDrawerToggle for AppCompat diff --git a/Xamarin.Forms.Platform.Android/Renderers/AndroidGIFImageParser.cs b/Xamarin.Forms.Platform.Android/Renderers/AndroidGIFImageParser.cs index aa950008aa3..7689f5b2c39 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/AndroidGIFImageParser.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/AndroidGIFImageParser.cs @@ -194,7 +194,7 @@ public override bool SelectDrawable(int index) InJustDecodeBounds = true }; - int drawableIdentifier = ResourceManager.GetDrawableByName(file); + int drawableIdentifier = context.GetDrawableId(file); if (drawableIdentifier != 0) { diff --git a/Xamarin.Forms.Platform.Android/ResourceManager.cs b/Xamarin.Forms.Platform.Android/ResourceManager.cs index c6037561c3f..089991b6e01 100644 --- a/Xamarin.Forms.Platform.Android/ResourceManager.cs +++ b/Xamarin.Forms.Platform.Android/ResourceManager.cs @@ -29,7 +29,7 @@ public static class ResourceManager static Assembly _assembly; static Type FindType(string name, string altName) { - return _assembly.GetTypes().FirstOrDefault(x => x.Name == name || x.Name == altName); + return _assembly?.GetTypes().FirstOrDefault(x => x.Name == name || x.Name == altName); } static Type _drawableClass; static Type _resourceClass; @@ -93,7 +93,7 @@ public static Type LayoutClass { if (imageSource is FileImageSource fileImageSource) { var file = fileImageSource.File; - var id = IdFromTitle(file, DrawableClass); + var id = IdFromTitle(file, DrawableClass, "drawable", context); // try the drawables via id if (id != 0) @@ -353,19 +353,35 @@ public static Drawable GetDrawable(this Context context, string name) return AndroidAppCompat.GetDrawable(context, id); } + public static int GetDrawableId(this Context context, string title) + { + return IdFromTitle(title, DrawableClass, _drawableDefType, context); + } + + [Obsolete("GetDrawableByName(string) is obsolete as of version 4.8. " + + "Please use GetDrawableId(string, context) instead.")] public static int GetDrawableByName(string name) { - return IdFromTitle(name, DrawableClass); + return IdFromTitle(name, DrawableClass, _drawableDefType, Forms.ApplicationContext); } + [Obsolete("GetResourceByName(string) is obsolete as of version 4.8. " + + "Please use GetResource(string, context) instead.")] public static int GetResourceByName(string name) { - return IdFromTitle(name, ResourceClass); + return IdFromTitle(name, ResourceClass, "id", Forms.ApplicationContext); + } + + public static int GetResource(this Context context, string title) + { + return IdFromTitle(title, ResourceClass, "id", context); } + [Obsolete("GetLayoutByName(string) is obsolete as of version 4.8. " + + "Please use GetLayout(string, context) instead.")] public static int GetLayoutByName(string name) { - return IdFromTitle(name, LayoutClass); + return IdFromTitle(name, LayoutClass, "layout", Forms.ApplicationContext); } public static int GetLayout(this Context context, string name) @@ -373,9 +389,11 @@ public static int GetLayout(this Context context, string name) return IdFromTitle(name, LayoutClass, "layout", context); } + [Obsolete("GetStyleByName(string) is obsolete as of version 4.8. " + + "Please use GetStyle(string, context) instead.")] public static int GetStyleByName(string name) { - return IdFromTitle(name, StyleClass); + return IdFromTitle(name, StyleClass, "style", Forms.ApplicationContext); } public static int GetStyle(this Context context, string name) @@ -388,16 +406,6 @@ public static void Init(Assembly masterAssembly) _assembly = masterAssembly; } - static int IdFromTitle(string title, Type type) - { - if (title == null) - return 0; - - string name = IOPath.GetFileNameWithoutExtension(title); - int id = GetId(type, name); - return id; - } - static int IdFromTitle(string title, Type resourceType, string defType, Resources resource) { return IdFromTitle(title, resourceType, defType, resource, AppCompat.Platform.GetPackageName()); @@ -405,7 +413,7 @@ static int IdFromTitle(string title, Type resourceType, string defType, Resource static int IdFromTitle(string title, Type resourceType, string defType, Context context) { - return IdFromTitle(title, resourceType, defType, context.Resources, context.PackageName); + return IdFromTitle(title, resourceType, defType, context?.Resources, context?.PackageName); } static int IdFromTitle(string title, Type resourceType, string defType, Resources resource, string packageName) @@ -414,24 +422,38 @@ static int IdFromTitle(string title, Type resourceType, string defType, Resource if (title == null) return id; - string name = IOPath.GetFileNameWithoutExtension(title); + string name; - id = GetId(resourceType, name); + if (defType == "style" || (resourceType != null && resourceType == StyleClass)) + name = title; + else + name = title.ToLower(); - if (id > 0) + if (defType == _drawableDefType || (resourceType != null && resourceType == DrawableClass)) + name = IOPath.GetFileNameWithoutExtension(name); + + if ((id = SearchByIdentifier(name, defType, resource, packageName)) > 0) return id; - if (packageName != null) + // When searching by reflection you would use a "_" instead of a "." + // So this accounts for cases where users were searching with an "_" + if ((id = SearchByIdentifier(name.Replace("_", "."), defType, resource, packageName)) > 0) + return id; + + int SearchByIdentifier(string n, string d, Resources r, string p) { - id = resource.GetIdentifier(name, defType, packageName); + int returnValue = 0; - if (id > 0) - return id; - } + if (p != null) + returnValue = r.GetIdentifier(n, d, p); - id = resource.GetIdentifier(name, defType, null); + if (returnValue == 0) + returnValue = r.GetIdentifier(n, d, null); + + return returnValue; + } - return id; + return GetId(resourceType, name); } static int GetId(Type type, string memberName)