示例#1
0
        public override bool Execute()
        {
            AndroidManifest manifest;

            using (var stream = GetType().Assembly.GetManifestResourceStream("Xamarin.Android.Lite.Tasks.AndroidManifest.xml")) {
                manifest = AndroidManifest.Read(stream);
            }

            string versionCode = string.IsNullOrEmpty(VersionCode) ? "1" : VersionCode;
            string versionName = string.IsNullOrEmpty(VersionName) ? "1.0" : VersionName;

            var manifestElement = manifest.Document;

            if (manifestElement.Name != "manifest")
            {
                Log.LogError("No `manifest` element found!");
                return(false);
            }
            var application = manifestElement.Element("application");

            if (application == null)
            {
                Log.LogError("No `application` element found!");
                return(false);
            }

            var ns   = AndroidManifest.AndroidNamespace.Namespace;
            var name = ns + "name";

            manifest.Mutate(manifestElement, "package", PackageName);
            manifest.Mutate(manifestElement, ns + "versionCode", versionCode);
            manifest.Mutate(manifestElement, ns + "versionName", versionName);

            var metadata = application.Elements("meta-data").Where(e => e.Attribute(name)?.Value == ApplicationMetadata).FirstOrDefault();

            if (metadata == null)
            {
                Log.LogError("No `meta-data` element found!");
                return(false);
            }
            manifest.Mutate(metadata, ns + "value", ApplicationClass);

            //NOTE: two other Xamarin.Android implementation-specific places *may* need the package name replaced
            var provider = application.Element("provider");

            if (provider != null)
            {
                manifest.Mutate(provider, ns + "authorities", PackageName + ".mono.MonoRuntimeProvider.__mono_init_");
            }

            var category = application.Elements("receiver")
                           .Where(e => e.Attribute(name)?.Value == "mono.android.Seppuku")
                           .FirstOrDefault()?
                           .Element("intent-filter")?
                           .Element("category");

            if (category != null)
            {
                manifest.Mutate(category, name, "mono.android.intent.category.SEPPUKU." + PackageName);
            }

            using (var stream = File.Create(DestinationFile)) {
                manifest.Write(stream);
            }

            return(!Log.HasLoggedErrors);
        }
        /// <summary>
        /// NOTE: does not dispose the stream
        /// </summary>
        /// <param name="onChunk">Mainly used for testing, verifying correctness in tests</param>
        public static AndroidManifest Read(Stream stream, Action <ChunkType, int, long> onChunk = null)
        {
            var      manifest   = new AndroidManifest();
            var      namespaces = new Dictionary <string, XName> ();
            XElement xml        = null;

            var buffer      = new byte [4];
            var stringTable =
                manifest.Strings = new List <string> ();
            int chunk;

            while ((chunk = ReadInt(buffer, stream)) != -1)
            {
                int chunkSize = ReadInt(buffer, stream);                  //length of stream
                onChunk?.Invoke((ChunkType)chunk, chunkSize, stream.Position);

                switch ((ChunkType)chunk)
                {
                case ChunkType.START_DOC: {
                    //Don't need to do anything
                    break;
                }

                case ChunkType.STR_TABLE: {
                    int stringCount   = ReadInt(buffer, stream);
                    int styleCount    = ReadInt(buffer, stream);
                    int flags         = ReadInt(buffer, stream);
                    int stringsOffset = ReadInt(buffer, stream);
                    int stylesOffset  = ReadInt(buffer, stream);

                    var    stringOffsets = ReadArray(buffer, stream, stringCount);
                    int [] styleOffsets  = ReadArray(buffer, stream, styleCount);
                    int    size          = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset;
                    var    strings       = new byte [size];
                    stream.Read(strings, 0, size);

                    for (int i = 0; i < stringOffsets.Length; i++)
                    {
                        stringTable.Add(GetString(stringOffsets [i], strings));
                    }
                    break;
                }

                case ChunkType.RESOURCES: {
                    int [] resources = ReadArray(buffer, stream, chunkSize / 4 - 2);
                    manifest.Resources = new List <int> (resources);
                    break;
                }

                case ChunkType.NS_TABLE: {
                    int namespaceCount = ReadInt(buffer, stream);
                    int dunno          = ReadInt(buffer, stream);                             //0xFFFFFFF

                    int [] namespaceData = ReadArray(buffer, stream, namespaceCount);
                    for (int i = 0; i < namespaceCount - 1; i += 2)
                    {
                        var name = stringTable [namespaceData [i]];
                        var url  = stringTable [namespaceData [i + 1]];
                        namespaces [url] = XNamespace.Get(url) + name;
                    }
                    break;
                }

                case ChunkType.START_TAG: {
                    if (stringTable.Count == 0)
                    {
                        throw new InvalidOperationException($"Unexpected file format, {nameof (ChunkType.STR_TABLE)} not found!");
                    }

                    int lineNumber = ReadInt(buffer, stream);
                    int dunno      = ReadInt(buffer, stream);                                 //0xFFFFFFFF

                    int ns             = ReadInt(buffer, stream);
                    int name           = ReadInt(buffer, stream);
                    int flags          = ReadInt(buffer, stream);
                    int attributeCount = ReadInt(buffer, stream) & 0xFFFF;
                    int classAttribute = ReadInt(buffer, stream);

                    string nameText = stringTable [name];
                    if (xml == null)
                    {
                        manifest.Document = xml = new XElement(nameText);

                        foreach (var knownNs in namespaces)
                        {
                            xml.SetAttributeValue(XNamespace.Xmlns + knownNs.Value.LocalName, knownNs.Value.NamespaceName);
                        }
                    }
                    else
                    {
                        var child = new XElement(nameText);
                        xml.Add(child);
                        xml = child;
                    }

                    for (int i = 0; i < attributeCount; i++)
                    {
                        int attrNs    = ReadInt(buffer, stream);
                        int attrName  = ReadInt(buffer, stream);
                        int attrValue = ReadInt(buffer, stream);
                        int attrType  = ReadInt(buffer, stream);
                        int attrData  = ReadInt(buffer, stream);

                        string attrNameText = stringTable [attrName];
                        XName  attributeName;
                        if (attrNs == -1)
                        {
                            attributeName = attrNameText;
                        }
                        else
                        {
                            var nsUrl = stringTable [attrNs];
                            attributeName = XName.Get(attrNameText, nsUrl);
                        }
                        XAttribute newAttr;
                        switch ((AttributeType)attrType)
                        {
                        case AttributeType.Integer:
                            newAttr = new XAttribute(attributeName, attrData);
                            break;

                        case AttributeType.String:
                            newAttr = new XAttribute(attributeName, stringTable [attrData]);
                            break;

                        case AttributeType.Resource:
                            //TODO: need the string instead?
                            newAttr = new XAttribute(attributeName, attrData);
                            break;

                        case AttributeType.Enum:
                            newAttr = new XAttribute(attributeName, attrData);
                            break;

                        case AttributeType.Bool:
                            //NOTE: looks like this is -1=True 0=False ???
                            newAttr = new XAttribute(attributeName, attrData == -1);
                            break;

                        default:
                            newAttr = new XAttribute(attributeName, $"[Unknown Data Type: {attrType.ToString ("X")}, Value: {attrData}]");
                            break;
                        }
                        newAttr.AddAnnotation(attrType);
                        xml.Add(newAttr);
                    }
                    break;
                }

                case ChunkType.END_TAG: {
                    int lineNumber = ReadInt(buffer, stream);
                    int dunno      = ReadInt(buffer, stream);                                 //0xFFFFFFFF
                    int ns         = ReadInt(buffer, stream);
                    int name       = ReadInt(buffer, stream);
                    xml = xml.Parent;
                    break;
                }

                case ChunkType.END_DOC: {
                    int    fileVersion = ReadInt(buffer, stream);
                    int [] dunno       = ReadArray(buffer, stream, 3);                                //-1, android, NS url
                    manifest.PlatformBuildVersionName = stringTable [fileVersion];
                    break;
                }

                default:
                    throw new InvalidOperationException($"Invalid chunk `{chunk.ToString ("X")}` at position `{stream.Position}`!");
                }
            }

            return(manifest);
        }
示例#3
0
        public override bool Execute()
        {
            AndroidManifest manifest;

            using (var stream = GetType().Assembly.GetManifestResourceStream("Xamarin.Android.Lite.Tasks.AndroidManifest.xml")) {
                manifest = AndroidManifest.Read(stream);
            }

            string versionCode = string.IsNullOrEmpty(VersionCode) ? "1" : VersionCode;
            string versionName = string.IsNullOrEmpty(VersionName) ? "1.0" : VersionName;

            var manifestElement = manifest.Document;

            if (manifestElement.Name != "manifest")
            {
                Log.LogError("No `manifest` element found!");
                return(false);
            }
            var application = manifestElement.Element("application");

            if (application == null)
            {
                Log.LogError("No `application` element found!");
                return(false);
            }

            var ns   = AndroidManifest.AndroidNamespace.Namespace;
            var name = ns + "name";

            manifest.Mutate(manifestElement, "package", PackageName);
            manifest.Mutate(manifestElement, ns + "versionCode", versionCode);
            manifest.Mutate(manifestElement, ns + "versionName", versionName);

            if (!string.IsNullOrEmpty(AppTitle))
            {
                manifest.Mutate(application, ns + "label", AppTitle);
            }

            var metadata = application.Elements("meta-data").Where(e => e.Attribute(name)?.Value == ApplicationMetadata).FirstOrDefault();

            if (metadata == null)
            {
                Log.LogError("No `meta-data` element found!");
                return(false);
            }
            manifest.Mutate(metadata, ns + "value", ApplicationClass);

            var activity = application.Elements("activity").Where(e => e.Attribute(name)?.Value == ActivityName).FirstOrDefault();

            if (activity == null)
            {
                Log.LogError($"No `activity` element found of name `{ActivityName}`!");
                return(false);
            }
            if (!string.IsNullOrEmpty(ActivityTitle))
            {
                manifest.Mutate(activity, ns + "label", ActivityTitle);
            }

            foreach (var provider in application.Elements("provider"))
            {
                var authorities = ns + "authorities";
                var attribute   = provider.Attribute(authorities);
                if (attribute != null && attribute.Value.StartsWith(PackageNamePrefix, StringComparison.Ordinal))
                {
                    var value = PackageName + attribute.Value.Substring(PackageNamePrefix.Length - 1, attribute.Value.Length - PackageNamePrefix.Length + 1);
                    manifest.Mutate(provider, authorities, value);
                }
            }

            var category = application.Elements("receiver")
                           .Where(e => e.Attribute(name)?.Value == "mono.android.Seppuku")
                           .FirstOrDefault()?
                           .Element("intent-filter")?
                           .Element("category");

            if (category != null)
            {
                manifest.Mutate(category, name, "mono.android.intent.category.SEPPUKU." + PackageName);
            }

            using (var stream = File.Create(DestinationFile)) {
                manifest.Write(stream);
            }

            return(!Log.HasLoggedErrors);
        }