|
| 1 | +#nullable enable |
| 2 | + |
| 3 | +using System; |
| 4 | +using System.Collections.Generic; |
| 5 | +using System.Globalization; |
| 6 | +using System.Linq; |
| 7 | +using System.Xml.Linq; |
| 8 | + |
| 9 | +namespace Microsoft.Android.Sdk.TrimmableTypeMap; |
| 10 | + |
| 11 | +/// <summary> |
| 12 | +/// Adds assembly-level manifest elements (permissions, uses-permissions, uses-features, |
| 13 | +/// uses-library, uses-configuration, meta-data, property). |
| 14 | +/// </summary> |
| 15 | +static class AssemblyLevelElementBuilder |
| 16 | +{ |
| 17 | + static readonly XNamespace AndroidNs = ManifestConstants.AndroidNs; |
| 18 | + static readonly XName AttName = ManifestConstants.AttName; |
| 19 | + |
| 20 | + internal static void AddAssemblyLevelElements (XElement manifest, XElement app, AssemblyManifestInfo info) |
| 21 | + { |
| 22 | + var existingPermissions = new HashSet<string> ( |
| 23 | + manifest.Elements ("permission").Select (e => (string?)e.Attribute (AttName)).OfType<string> ()); |
| 24 | + var existingUsesPermissions = new HashSet<string> ( |
| 25 | + manifest.Elements ("uses-permission").Select (e => (string?)e.Attribute (AttName)).OfType<string> ()); |
| 26 | + |
| 27 | + // <permission> elements |
| 28 | + foreach (var perm in info.Permissions) { |
| 29 | + if (string.IsNullOrEmpty (perm.Name) || existingPermissions.Contains (perm.Name)) { |
| 30 | + continue; |
| 31 | + } |
| 32 | + var element = new XElement ("permission", new XAttribute (AttName, perm.Name)); |
| 33 | + PropertyMapper.MapDictionaryProperties (element, perm.Properties, "Label", "label"); |
| 34 | + PropertyMapper.MapDictionaryProperties (element, perm.Properties, "Description", "description"); |
| 35 | + PropertyMapper.MapDictionaryProperties (element, perm.Properties, "Icon", "icon"); |
| 36 | + PropertyMapper.MapDictionaryProperties (element, perm.Properties, "PermissionGroup", "permissionGroup"); |
| 37 | + PropertyMapper.MapDictionaryEnumProperty (element, perm.Properties, "ProtectionLevel", "protectionLevel", AndroidEnumConverter.ProtectionToString); |
| 38 | + manifest.Add (element); |
| 39 | + } |
| 40 | + |
| 41 | + // <permission-group> elements |
| 42 | + foreach (var pg in info.PermissionGroups) { |
| 43 | + if (string.IsNullOrEmpty (pg.Name)) { |
| 44 | + continue; |
| 45 | + } |
| 46 | + var element = new XElement ("permission-group", new XAttribute (AttName, pg.Name)); |
| 47 | + PropertyMapper.MapDictionaryProperties (element, pg.Properties, "Label", "label"); |
| 48 | + PropertyMapper.MapDictionaryProperties (element, pg.Properties, "Description", "description"); |
| 49 | + PropertyMapper.MapDictionaryProperties (element, pg.Properties, "Icon", "icon"); |
| 50 | + manifest.Add (element); |
| 51 | + } |
| 52 | + |
| 53 | + // <permission-tree> elements |
| 54 | + foreach (var pt in info.PermissionTrees) { |
| 55 | + if (string.IsNullOrEmpty (pt.Name)) { |
| 56 | + continue; |
| 57 | + } |
| 58 | + var element = new XElement ("permission-tree", new XAttribute (AttName, pt.Name)); |
| 59 | + PropertyMapper.MapDictionaryProperties (element, pt.Properties, "Label", "label"); |
| 60 | + PropertyMapper.MapDictionaryProperties (element, pt.Properties, "Icon", "icon"); |
| 61 | + manifest.Add (element); |
| 62 | + } |
| 63 | + |
| 64 | + // <uses-permission> elements |
| 65 | + foreach (var up in info.UsesPermissions) { |
| 66 | + if (string.IsNullOrEmpty (up.Name) || existingUsesPermissions.Contains (up.Name)) { |
| 67 | + continue; |
| 68 | + } |
| 69 | + var element = new XElement ("uses-permission", new XAttribute (AttName, up.Name)); |
| 70 | + if (up.MaxSdkVersion.HasValue) { |
| 71 | + element.SetAttributeValue (AndroidNs + "maxSdkVersion", up.MaxSdkVersion.Value.ToString (CultureInfo.InvariantCulture)); |
| 72 | + } |
| 73 | + manifest.Add (element); |
| 74 | + } |
| 75 | + |
| 76 | + // <uses-feature> elements |
| 77 | + var existingFeatures = new HashSet<string> ( |
| 78 | + manifest.Elements ("uses-feature").Select (e => (string?)e.Attribute (AttName)).OfType<string> ()); |
| 79 | + foreach (var uf in info.UsesFeatures) { |
| 80 | + if (uf.Name is not null && !existingFeatures.Contains (uf.Name)) { |
| 81 | + var element = new XElement ("uses-feature", |
| 82 | + new XAttribute (AttName, uf.Name), |
| 83 | + new XAttribute (AndroidNs + "required", uf.Required ? "true" : "false")); |
| 84 | + manifest.Add (element); |
| 85 | + } else if (uf.GLESVersion != 0) { |
| 86 | + var versionStr = $"0x{uf.GLESVersion:X8}"; |
| 87 | + if (!manifest.Elements ("uses-feature").Any (e => (string?)e.Attribute (AndroidNs + "glEsVersion") == versionStr)) { |
| 88 | + var element = new XElement ("uses-feature", |
| 89 | + new XAttribute (AndroidNs + "glEsVersion", versionStr), |
| 90 | + new XAttribute (AndroidNs + "required", uf.Required ? "true" : "false")); |
| 91 | + manifest.Add (element); |
| 92 | + } |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + // <uses-library> elements inside <application> |
| 97 | + foreach (var ul in info.UsesLibraries) { |
| 98 | + if (string.IsNullOrEmpty (ul.Name)) { |
| 99 | + continue; |
| 100 | + } |
| 101 | + if (!app.Elements ("uses-library").Any (e => (string?)e.Attribute (AttName) == ul.Name)) { |
| 102 | + app.Add (new XElement ("uses-library", |
| 103 | + new XAttribute (AttName, ul.Name), |
| 104 | + new XAttribute (AndroidNs + "required", ul.Required ? "true" : "false"))); |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + // Assembly-level <meta-data> inside <application> |
| 109 | + foreach (var md in info.MetaData) { |
| 110 | + if (string.IsNullOrEmpty (md.Name)) { |
| 111 | + continue; |
| 112 | + } |
| 113 | + if (!app.Elements ("meta-data").Any (e => (string?)e.Attribute (AndroidNs + "name") == md.Name)) { |
| 114 | + app.Add (ComponentElementBuilder.CreateMetaDataElement (md)); |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + // Assembly-level <property> inside <application> |
| 119 | + foreach (var prop in info.Properties) { |
| 120 | + if (string.IsNullOrEmpty (prop.Name)) { |
| 121 | + continue; |
| 122 | + } |
| 123 | + if (!app.Elements ("property").Any (e => (string?)e.Attribute (AndroidNs + "name") == prop.Name)) { |
| 124 | + var element = new XElement ("property", |
| 125 | + new XAttribute (AndroidNs + "name", prop.Name)); |
| 126 | + if (prop.Value is not null) { |
| 127 | + element.SetAttributeValue (AndroidNs + "value", prop.Value); |
| 128 | + } |
| 129 | + if (prop.Resource is not null) { |
| 130 | + element.SetAttributeValue (AndroidNs + "resource", prop.Resource); |
| 131 | + } |
| 132 | + app.Add (element); |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + // <uses-configuration> elements |
| 137 | + foreach (var uc in info.UsesConfigurations) { |
| 138 | + var element = new XElement ("uses-configuration"); |
| 139 | + if (uc.ReqFiveWayNav) { |
| 140 | + element.SetAttributeValue (AndroidNs + "reqFiveWayNav", "true"); |
| 141 | + } |
| 142 | + if (uc.ReqHardKeyboard) { |
| 143 | + element.SetAttributeValue (AndroidNs + "reqHardKeyboard", "true"); |
| 144 | + } |
| 145 | + if (uc.ReqKeyboardType is not null) { |
| 146 | + element.SetAttributeValue (AndroidNs + "reqKeyboardType", uc.ReqKeyboardType); |
| 147 | + } |
| 148 | + if (uc.ReqNavigation is not null) { |
| 149 | + element.SetAttributeValue (AndroidNs + "reqNavigation", uc.ReqNavigation); |
| 150 | + } |
| 151 | + if (uc.ReqTouchScreen is not null) { |
| 152 | + element.SetAttributeValue (AndroidNs + "reqTouchScreen", uc.ReqTouchScreen); |
| 153 | + } |
| 154 | + manifest.Add (element); |
| 155 | + } |
| 156 | + } |
| 157 | + |
| 158 | + internal static void ApplyApplicationProperties (XElement app, Dictionary<string, object?> properties) |
| 159 | + { |
| 160 | + PropertyMapper.ApplyMappings (app, properties, PropertyMapper.ApplicationPropertyMappings, skipExisting: true); |
| 161 | + } |
| 162 | + |
| 163 | + internal static void AddInternetPermission (XElement manifest) |
| 164 | + { |
| 165 | + if (!manifest.Elements ("uses-permission").Any (p => |
| 166 | + (string?)p.Attribute (AndroidNs + "name") == "android.permission.INTERNET")) { |
| 167 | + manifest.Add (new XElement ("uses-permission", |
| 168 | + new XAttribute (AndroidNs + "name", "android.permission.INTERNET"))); |
| 169 | + } |
| 170 | + } |
| 171 | +} |
0 commit comments