public static void FixupResourceFilenameAndLogCodedWarning(this TaskLoggingHelper log, string code, string message, string file, string resourceDir, Dictionary <string, string> resourceNameCaseMap) { var targetfile = FixupResourceFilename(file, resourceDir, resourceNameCaseMap); log.LogCodedWarning(code, file: targetfile, lineNumber: 0, message: message); }
public IList <string> Merge(List <TypeDefinition> subclasses, string applicationClass, bool embed, string bundledWearApplicationName, IEnumerable <string> mergedManifestDocuments) { string applicationName = ApplicationName; var manifest = doc.Root; if (manifest == null || manifest.Name != "manifest") { throw new Exception("Root element must be 'manifest'"); } var manifest_package = (string)manifest.Attribute("package"); if (!string.IsNullOrWhiteSpace(manifest_package)) { PackageName = manifest_package; } manifest.SetAttributeValue(XNamespace.Xmlns + "android", "http://schemas.android.com/apk/res/android"); if (manifest.Attribute(androidNs + "versionCode") == null) { manifest.SetAttributeValue(androidNs + "versionCode", "1"); } if (manifest.Attribute(androidNs + "versionName") == null) { manifest.SetAttributeValue(androidNs + "versionName", "1.0"); } app = CreateApplicationElement(manifest, applicationClass, subclasses); if (app.Attribute(androidNs + "label") == null && applicationName != null) { app.SetAttributeValue(androidNs + "label", applicationName); } var existingTypes = new HashSet <string> ( app.Descendants().Select(a => (string)a.Attribute(attName)).Where(v => v != null)); if (!string.IsNullOrEmpty(bundledWearApplicationName)) { if (!app.Elements("meta-data").Any(e => e.Attributes(androidNs + "name").Any(a => a.Value == bundledWearApplicationName))) { app.Add(new XElement("meta-data", new XAttribute(androidNs + "name", "com.google.android.wearable.beta.app"), new XAttribute(androidNs + "resource", "@xml/wearable_app_desc"))); } } // If no <uses-sdk> is specified, add it with both minSdkVersion and // targetSdkVersion set to TargetFrameworkVersion if (!manifest.Elements("uses-sdk").Any()) { manifest.AddFirst( new XElement("uses-sdk", new XAttribute(androidNs + "minSdkVersion", SdkVersionName), new XAttribute(androidNs + "targetSdkVersion", SdkVersionName))); } // If no minSdkVersion is specified, set it to TargetFrameworkVersion var uses = manifest.Element("uses-sdk"); if (uses.Attribute(androidNs + "minSdkVersion") == null) { int minSdkVersion; if (!int.TryParse(SdkVersionName, out minSdkVersion)) { minSdkVersion = XABuildConfig.NDKMinimumApiAvailable; } minSdkVersion = Math.Min(minSdkVersion, XABuildConfig.NDKMinimumApiAvailable); uses.SetAttributeValue(androidNs + "minSdkVersion", minSdkVersion.ToString()); } string targetSdkVersion; var tsv = uses.Attribute(androidNs + "targetSdkVersion"); if (tsv != null) { targetSdkVersion = tsv.Value; } else { targetSdkVersion = SdkVersionName; uses.AddBeforeSelf(new XComment("suppress UsesMinSdkAttributes")); } int?tryTargetSdkVersion = MonoAndroidHelper.SupportedVersions.GetApiLevelFromId(targetSdkVersion); if (!tryTargetSdkVersion.HasValue) { throw new InvalidOperationException(string.Format("The targetSdkVersion ({0}) is not a valid API level", targetSdkVersion)); } int targetSdkVersionValue = tryTargetSdkVersion.Value; foreach (var t in subclasses) { if (t.IsAbstract) { continue; } if (PackageName == null) { PackageName = t.Namespace; } var name = JavaNativeTypeManager.ToJniName(t).Replace('/', '.'); var compatName = JavaNativeTypeManager.ToCompatJniName(t).Replace('/', '.'); if (((string)app.Attribute(attName)) == compatName) { app.SetAttributeValue(attName, name); } Func <TypeDefinition, string, int, XElement> generator = GetGenerator(t); if (generator == null) { continue; } try { // activity not present: create a launcher for it IFF it has attribute if (!existingTypes.Contains(name) && !existingTypes.Contains(compatName)) { XElement fromCode = generator(t, name, targetSdkVersionValue); if (fromCode == null) { continue; } IEnumerable <MethodDefinition> constructors = t.Methods.Where(m => m.IsConstructor).Cast <MethodDefinition> (); if (!constructors.Any(c => !c.HasParameters && c.IsPublic)) { string message = $"The type '{t.FullName}' must provide a public default constructor"; SequencePoint sourceLocation = FindSource(constructors); if (sourceLocation != null && sourceLocation.Document?.Url != null) { log.LogError( subcategory: String.Empty, errorCode: "XA4213", helpKeyword: String.Empty, file: sourceLocation.Document.Url, lineNumber: sourceLocation.StartLine, columnNumber: sourceLocation.StartColumn, endLineNumber: sourceLocation.EndLine, endColumnNumber: sourceLocation.EndColumn, message: message); } else { log.LogCodedError("XA4213", message); } continue; } app.Add(fromCode); } foreach (var d in app.Descendants().Where(a => ((string)a.Attribute(attName)) == compatName)) { d.SetAttributeValue(attName, name); } } catch (InvalidActivityNameException ex) { log.LogErrorFromException(ex); } } var icon = app.Attribute(androidNs + "icon"); if (icon == null) { var activity = app.Element("activity"); if (activity != null) { var activityIcon = activity.Attribute(androidNs + "icon"); if (activityIcon != null) { app.Add(new XAttribute(androidNs + "icon", activityIcon.Value)); } } } PackageName = AndroidAppManifest.CanonicalizePackageName(PackageName); if (!PackageName.Contains('.')) { throw new InvalidOperationException("/manifest/@package attribute MUST contain a period ('.')."); } manifest.SetAttributeValue("package", PackageName); if (MultiDex) { app.Add(CreateMonoRuntimeProvider("mono.android.MultiDexLoader", null, initOrder: --AppInitOrder)); } var providerNames = AddMonoRuntimeProviders(app); if (Debug && !embed && InstantRunEnabled) { if (int.TryParse(SdkVersion, out int apiLevel) && apiLevel >= 19) { app.Add(CreateMonoRuntimeProvider("mono.android.ResourcePatcher", null, initOrder: --AppInitOrder)); } } if (Debug) { app.Add(new XComment("suppress ExportedReceiver")); app.Add(new XElement("receiver", new XAttribute(androidNs + "name", "mono.android.Seppuku"), new XElement("intent-filter", new XElement("action", new XAttribute(androidNs + "name", "mono.android.intent.action.SEPPUKU")), new XElement("category", new XAttribute(androidNs + "name", "mono.android.intent.category.SEPPUKU." + PackageName))))); if (app.Attribute(androidNs + "debuggable") == null) { app.Add(new XAttribute(androidNs + "debuggable", "true")); } } if (Debug || NeedsInternet) { AddInternetPermissionForDebugger(); } if (!embed) { AddFastDeployPermissions(); } // If the manifest has android:installLocation, but we are targeting // API 7 or lower, remove it for the user and show a warning if (manifest.Attribute(androidNs + "installLocation") != null) { if (targetSdkVersionValue < 8) { manifest.Attribute(androidNs + "installLocation").Remove(); Console.Error.WriteLine("monodroid: warning 1 : installLocation cannot be specified for Android versions less than 2.2. Attribute installLocation ignored."); } } AddInstrumentations(manifest, subclasses, targetSdkVersionValue); AddPermissions(app); AddPermissionGroups(app); AddPermissionTrees(app); AddUsesPermissions(app); AddUsesFeatures(app); AddSupportsGLTextures(app); ReorderActivityAliases(app); ReorderElements(app); if (mergedManifestDocuments != null) { foreach (var mergedManifest in mergedManifestDocuments) { try { MergeLibraryManifest(mergedManifest); } catch (Exception ex) { log.LogCodedWarning("XA4302", "Unhandled exception merging `AndroidManifest.xml`: {0}", ex); } } } return(providerNames); SequencePoint FindSource(IEnumerable <MethodDefinition> methods) { if (methods == null) { return(null); } SequencePoint ret = null; foreach (MethodDefinition method in methods.Where(m => m != null && m.HasBody && m.DebugInformation != null)) { foreach (Instruction ins in method.Body.Instructions) { SequencePoint seq = method.DebugInformation.GetSequencePoint(ins); if (seq == null) { continue; } if (ret == null || seq.StartLine < ret.StartLine) { ret = seq; } break; } } return(ret); } }