From 5a1b9b1e2b9598ecaebe4ccd02b69055a020cf84 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Fri, 18 Aug 2023 12:47:30 -0500 Subject: [PATCH] [Mono.Android] [IntentFilter] pathSuffix & pathAdvancedPattern (#8261) Fixes: https://github.com/xamarin/xamarin-android/issues/8235 Context: https://github.com/xamarin/xamarin-android/issues/8272 Add the following properties to `IntentFilterAttribute` to allow the various `AndroidManifest.xml` [``][0] attributes to be generated: - `IntentFilterAttribute.DataPathSuffix` generates [`//intent-filter/data/@pathSuffix`][1]. - `IntentFilterAttribute.DataPathSuffixes` generates [`//intent-filter/data/@pathSuffix`][1]. - `IntentFilterAttribute.DataPathAdvancedPattern` generates [`//intent-filter/data/@pathAdvancedPattern`][1]. - `IntentFilterAttribute.DataPathAdvancedPatterns` generates [`//intent-filter/data/@pathAdvancedPattern`][1]. Note that while we have a script to detect new elements added to `AndroidManifest.xml`, the code must be written manually. TODO: Issue #8272 to automate this code generation. [0]: https://developer.android.com/guide/topics/manifest/data-element [1]: https://developer.android.com/guide/topics/manifest/data-element#path --- .../manifest-definition.xml | 1 + .../Android.App/IntentFilterAttribute.cs | 8 +++ .../PublicAPI/API-34/PublicAPI.Unshipped.txt | 8 +++ .../IntentFilterAttribute.Partial.cs | 20 ++++++-- .../ManifestTest.cs | 51 +++++++++++++++++++ .../Utilities/BuildHelper.cs | 9 ++++ .../Xamarin.Android.Build.Tasks.csproj | 2 +- 7 files changed, 94 insertions(+), 5 deletions(-) diff --git a/build-tools/manifest-attribute-codegen/manifest-definition.xml b/build-tools/manifest-attribute-codegen/manifest-definition.xml index 98b4a6d69d2..527b74f748e 100644 --- a/build-tools/manifest-attribute-codegen/manifest-definition.xml +++ b/build-tools/manifest-attribute-codegen/manifest-definition.xml @@ -345,6 +345,7 @@ + intent-filter diff --git a/src/Mono.Android/Android.App/IntentFilterAttribute.cs b/src/Mono.Android/Android.App/IntentFilterAttribute.cs index 08a849665c3..ea9847520d9 100644 --- a/src/Mono.Android/Android.App/IntentFilterAttribute.cs +++ b/src/Mono.Android/Android.App/IntentFilterAttribute.cs @@ -42,6 +42,14 @@ public IntentFilterAttribute (string[] actions) #endif #if ANDROID_25 public string? RoundIcon {get; set;} +#endif +#if ANDROID_26 + public string? DataPathAdvancedPattern {get; set;} + public string[]? DataPathAdvancedPatterns {get; set;} +#endif +#if ANDROID_31 + public string? DataPathSuffix {get; set;} + public string[]? DataPathSuffixes {get; set;} #endif } } diff --git a/src/Mono.Android/PublicAPI/API-34/PublicAPI.Unshipped.txt b/src/Mono.Android/PublicAPI/API-34/PublicAPI.Unshipped.txt index 90896b5a82b..b4b1687d606 100644 --- a/src/Mono.Android/PublicAPI/API-34/PublicAPI.Unshipped.txt +++ b/src/Mono.Android/PublicAPI/API-34/PublicAPI.Unshipped.txt @@ -5215,6 +5215,10 @@ Android.App.IntentFilterAttribute.DataMimeTypes.get -> string![]? Android.App.IntentFilterAttribute.DataMimeTypes.set -> void Android.App.IntentFilterAttribute.DataPath.get -> string? Android.App.IntentFilterAttribute.DataPath.set -> void +Android.App.IntentFilterAttribute.DataPathAdvancedPattern.get -> string? +Android.App.IntentFilterAttribute.DataPathAdvancedPattern.set -> void +Android.App.IntentFilterAttribute.DataPathAdvancedPatterns.get -> string![]? +Android.App.IntentFilterAttribute.DataPathAdvancedPatterns.set -> void Android.App.IntentFilterAttribute.DataPathPattern.get -> string? Android.App.IntentFilterAttribute.DataPathPattern.set -> void Android.App.IntentFilterAttribute.DataPathPatterns.get -> string![]? @@ -5225,6 +5229,10 @@ Android.App.IntentFilterAttribute.DataPathPrefixes.get -> string![]? Android.App.IntentFilterAttribute.DataPathPrefixes.set -> void Android.App.IntentFilterAttribute.DataPaths.get -> string![]? Android.App.IntentFilterAttribute.DataPaths.set -> void +Android.App.IntentFilterAttribute.DataPathSuffix.get -> string? +Android.App.IntentFilterAttribute.DataPathSuffix.set -> void +Android.App.IntentFilterAttribute.DataPathSuffixes.get -> string![]? +Android.App.IntentFilterAttribute.DataPathSuffixes.set -> void Android.App.IntentFilterAttribute.DataPort.get -> string? Android.App.IntentFilterAttribute.DataPort.set -> void Android.App.IntentFilterAttribute.DataPorts.get -> string![]? diff --git a/src/Xamarin.Android.Build.Tasks/Mono.Android/IntentFilterAttribute.Partial.cs b/src/Xamarin.Android.Build.Tasks/Mono.Android/IntentFilterAttribute.Partial.cs index 381ba53c64d..db765b53b2a 100644 --- a/src/Xamarin.Android.Build.Tasks/Mono.Android/IntentFilterAttribute.Partial.cs +++ b/src/Xamarin.Android.Build.Tasks/Mono.Android/IntentFilterAttribute.Partial.cs @@ -27,6 +27,8 @@ partial class IntentFilterAttribute { { "DataPort", "port" }, { "DataScheme", "scheme" }, { "AutoVerify", "autoVerify" }, + { "DataPathSuffix", "pathSuffix" }, + { "DataPathAdvancedPattern", "pathAdvancedPattern" }, }; static readonly Dictionary> setters = new Dictionary> () { @@ -50,6 +52,10 @@ partial class IntentFilterAttribute { { "DataSchemes", (self, value) => self.DataSchemes = ToStringArray (value) }, { "AutoVerify", (self, value) => self._AutoVerify = (bool) value }, { "RoundIcon", (self, value) => self._RoundIcon = (string) value }, + { "DataPathSuffix", (self, value) => self.DataPathSuffix = (string) value }, + { "DataPathSuffixes", (self, value) => self.DataPathSuffixes = ToStringArray (value) }, + { "DataPathAdvancedPattern", (self, value) => self.DataPathAdvancedPattern = (string) value }, + { "DataPathAdvancedPatterns", (self, value) => self.DataPathAdvancedPatterns = ToStringArray (value) }, }; static string[] ToStringArray (object value) @@ -126,6 +132,8 @@ IEnumerable GetData (string packageName) Func toPathPrefix = v => ToAttribute ("DataPathPrefix", ReplacePackage (v, packageName)); Func toPort = v => ToAttribute ("DataPort", ReplacePackage (v, packageName)); Func toScheme = v => ToAttribute ("DataScheme", ReplacePackage (v, packageName)); + Func toPathSuffix = v => ToAttribute ("DataPathSuffix", ReplacePackage (v, packageName)); + Func toPathAdvancedPattern = v => ToAttribute ("DataPathAdvancedPattern", ReplacePackage (v, packageName)); Func, string, XElement> toData = (f, s) => string.IsNullOrEmpty (s) ? null : new XElement ("data", f (s)); var empty = Array.Empty (); var dataList = Enumerable.Empty () @@ -135,11 +143,13 @@ IEnumerable GetData (string packageName) .Concat ((DataPathPatterns ?? empty).Select (p => toData (toPathPattern, p))) .Concat ((DataPathPrefixes ?? empty).Select (p => toData (toPathPrefix, p))) .Concat ((DataPorts ?? empty).Select (p => toData (toPort, p))) - .Concat ((DataSchemes ?? empty).Select (p => toData (toScheme, p))); + .Concat ((DataSchemes ?? empty).Select (p => toData (toScheme, p))) + .Concat ((DataPathSuffixes ?? empty).Select (p => toData (toPathSuffix, p))) + .Concat ((DataPathAdvancedPatterns ?? empty).Select (p => toData (toPathAdvancedPattern, p))); if (string.IsNullOrEmpty (DataHost) && string.IsNullOrEmpty (DataMimeType) && string.IsNullOrEmpty (DataPath) && string.IsNullOrEmpty (DataPathPattern) && string.IsNullOrEmpty (DataPathPrefix) && - string.IsNullOrEmpty (DataPort) && string.IsNullOrEmpty (DataScheme) && - !dataList.Any ()) + string.IsNullOrEmpty (DataPort) && string.IsNullOrEmpty (DataScheme) && string.IsNullOrEmpty (DataPathSuffix) && + string.IsNullOrEmpty (DataPathAdvancedPattern) && !dataList.Any ()) return null; return new XElement [] { toData (toHost, DataHost), @@ -148,7 +158,9 @@ IEnumerable GetData (string packageName) toData (toPathPattern, DataPathPattern), toData (toPathPrefix, DataPathPrefix), toData (toPort, DataPort), - toData (toScheme, DataScheme) } + toData (toScheme, DataScheme), + toData (toPathSuffix, DataPathSuffix), + toData (toPathAdvancedPattern, DataPathAdvancedPattern)} .Concat (dataList).Where (x => x != null); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs index 03088e4a2ee..1b97abb515d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs @@ -10,6 +10,9 @@ using System.Collections.Generic; using Xamarin.Android.Tasks; using Xamarin.Android.Tools; +using Android.App; +using Mono.Cecil; +using System.Reflection; namespace Xamarin.Android.Build.Tests { @@ -1118,5 +1121,53 @@ public void SupportedOSPlatformVersionErrors (string minSdkVersion, string suppo } } + [IntentFilter (new [] { "singularAction" }, + DataPathSuffix = "singularSuffix", + DataPathAdvancedPattern = "singularPattern")] + [IntentFilter (new [] { "pluralAction" }, + DataPathSuffixes = new [] { "pluralSuffix1", "pluralSuffix2" }, + DataPathAdvancedPatterns = new [] { "pluralPattern1", "pluralPattern2" })] + + public class IntentFilterAttributeDataPathTestClass { } + + [Test] + public void IntentFilterDataPathTest () + { + var asm = AssemblyDefinition.ReadAssembly (typeof (IntentFilterAttributeDataPathTestClass).Assembly.Location); + var type = asm.MainModule.GetType ("Xamarin.Android.Build.Tests.ManifestTest/IntentFilterAttributeDataPathTestClass"); + + var intent = IntentFilterAttribute.FromTypeDefinition (type).Single (f => f.Actions.Contains ("singularAction")); + var xml = intent.ToElement ("dummy.packageid").ToString (); + + var expected = +@" + + + +"; + + StringAssertEx.AreMultiLineEqual (expected, xml); + } + + [Test] + public void IntentFilterDataPathsTest () + { + var asm = AssemblyDefinition.ReadAssembly (typeof (IntentFilterAttributeDataPathTestClass).Assembly.Location); + var type = asm.MainModule.GetType ("Xamarin.Android.Build.Tests.ManifestTest/IntentFilterAttributeDataPathTestClass"); + + var intent = IntentFilterAttribute.FromTypeDefinition (type).Single (f => f.Actions.Contains ("pluralAction")); + var xml = intent.ToElement ("dummy.packageid").ToString (); + + var expected = +@" + + + + + +"; + + StringAssertEx.AreMultiLineEqual (expected, xml); + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BuildHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BuildHelper.cs index b721e66e406..7c79ceef2c9 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BuildHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/BuildHelper.cs @@ -93,6 +93,15 @@ public static bool ContainsOccurances (this IEnumerable collection, stri } return found == count; } + + // Checks if two string are equal after normalizing string line endings + public static void AreMultiLineEqual (string expected, string actual) + { + expected = expected.ReplaceLineEndings (); + actual = actual.ReplaceLineEndings (); + + Assert.AreEqual (expected, actual); + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj index d392b3846ab..3003ed96c68 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj @@ -13,7 +13,7 @@ true $(MicrosoftAndroidSdkOutDir) false - $(DefineConstants);TRACE;HAVE_CECIL;MSBUILD;ANDROID_24 + $(DefineConstants);TRACE;HAVE_CECIL;MSBUILD;ANDROID_24;ANDROID_26;ANDROID_31 ..\..\src\Mono.Android\obj\$(Configuration)\$(DotNetTargetFramework)\android-$(AndroidLatestStablePlatformId)\mcw 8632 false