13

Im currently trying to program a little game for android with Unity3D. Because I want a visible status bar, I modified the AndroidManifest in the Project Folder (C:\Users\Public\Documents\Unity Projects\ProjectName\Temp\StagingArea) like this :

<application android:theme="Theme.Light.NoTitleBar" android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false" android:isGame="true" android:banner="@drawable/app_banner">

But everytime i generate the APK, unity changes the Android Manifest to this :

<application android:theme="@style/UnityThemeSelector" android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false" android:isGame="true" android:banner="@drawable/app_banner">

Unity3D does change my modified Manifests everytime?

Programmer
  • 121,791
  • 22
  • 236
  • 328
genaray
  • 1,080
  • 1
  • 9
  • 30

4 Answers4

28

You are modifying the wrong AndroidManifest file. That AndroidManifest from <ProjectName>\Temp\StagingArea you are modifying is generated by unity each time you build your project.

To use a custom AndroidManifest file, you have to put your custom AndroidManifest file at <ProjectName>Assets\Plugins\Android.

1.Go to <UnityInstallationDirecory>\Editor\Data\PlaybackEngines\AndroidPlayer\Apk, Copy the AndroidManifest.xml file to your <ProjectName>Assets\Plugins\Android

2.Open the copied Manifest file from <ProjectName>Assets\Plugins\Android and add your manifest.

In your particular case, add <application android:theme="Theme.Light.NoTitleBar" android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false" android:isGame="true" android:banner="@drawable/app_banner"> to it. Save, Build and Run.

Unity will now use that AndroidManifest file. If you get crash or any other problem, then Unity does not want you to change that.

Programmer
  • 121,791
  • 22
  • 236
  • 328
14

Starting with Unity 2018 you should implement the IPostGenerateGradleAndroidProject interface which will allow you to edit the android manifest after Unity produces it. In the code below I have added methods to set the microphone permission, set hardware acceleration, and set your application theme ( replace SetMicrophonePermission() to your preferred method, which is called when Unity calls OnPostGenerateGradleAndroidProject()).

Add the following code to Assets/Editor/ModifyUnityAndroidAppManifestSample.cs

using System.IO;
using System.Text;
using System.Xml;
using UnityEditor.Android;

public class ModifyUnityAndroidAppManifestSample : IPostGenerateGradleAndroidProject
{

    public void OnPostGenerateGradleAndroidProject(string basePath)
    {
        // If needed, add condition checks on whether you need to run the modification routine.
        // For example, specific configuration/app options enabled

        var androidManifest = new AndroidManifest(GetManifestPath(basePath));

        androidManifest.SetMicrophonePermission();

        // Add your XML manipulation routines

        androidManifest.Save();
    }

    public int callbackOrder { get { return 1; } }

    private string _manifestFilePath;

    private string GetManifestPath(string basePath)
    {
        if (string.IsNullOrEmpty(_manifestFilePath))
        {
            var pathBuilder = new StringBuilder(basePath);
            pathBuilder.Append(Path.DirectorySeparatorChar).Append("src");
            pathBuilder.Append(Path.DirectorySeparatorChar).Append("main");
            pathBuilder.Append(Path.DirectorySeparatorChar).Append("AndroidManifest.xml");
            _manifestFilePath = pathBuilder.ToString();
        }
        return _manifestFilePath;
    }
}


internal class AndroidXmlDocument : XmlDocument
{
    private string m_Path;
    protected XmlNamespaceManager nsMgr;
    public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
    public AndroidXmlDocument(string path)
    {
        m_Path = path;
        using (var reader = new XmlTextReader(m_Path))
        {
            reader.Read();
            Load(reader);
        }
        nsMgr = new XmlNamespaceManager(NameTable);
        nsMgr.AddNamespace("android", AndroidXmlNamespace);
    }

    public string Save()
    {
        return SaveAs(m_Path);
    }

    public string SaveAs(string path)
    {
        using (var writer = new XmlTextWriter(path, new UTF8Encoding(false)))
        {
            writer.Formatting = Formatting.Indented;
            Save(writer);
        }
        return path;
    }
}


internal class AndroidManifest : AndroidXmlDocument
{
    private readonly XmlElement ApplicationElement;

    public AndroidManifest(string path) : base(path)
    {
        ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement;
    }

    private XmlAttribute CreateAndroidAttribute(string key, string value)
    {
        XmlAttribute attr = CreateAttribute("android", key, AndroidXmlNamespace);
        attr.Value = value;
        return attr;
    }

    internal XmlNode GetActivityWithLaunchIntent()
    {
        return SelectSingleNode("/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and " +
                "intent-filter/category/@android:name='android.intent.category.LAUNCHER']", nsMgr);
    }

    internal void SetApplicationTheme(string appTheme)
    {
        ApplicationElement.Attributes.Append(CreateAndroidAttribute("theme", appTheme));
    }

    internal void SetStartingActivityName(string activityName)
    {
        GetActivityWithLaunchIntent().Attributes.Append(CreateAndroidAttribute("name", activityName));
    }


    internal void SetHardwareAcceleration()
    {
        GetActivityWithLaunchIntent().Attributes.Append(CreateAndroidAttribute("hardwareAccelerated", "true"));
    }

    internal void SetMicrophonePermission()
    {
        var manifest = SelectSingleNode("/manifest");
        XmlElement child = CreateElement("uses-permission");
        manifest.AppendChild(child);
        XmlAttribute newAttribute = CreateAndroidAttribute("name", "android.permission.RECORD_AUDIO");
        child.Attributes.Append(newAttribute);
    }
}
pale bone
  • 1,746
  • 2
  • 19
  • 26
  • 1
    I wrote a library that wraps around the code in this answer (in a limited manner): https://github.com/starikcetin/Eflatun.AndroidManifestHook – starikcetin Nov 01 '19 at 23:36
  • i meant my comment below to the main question to actually be a reply to this answer. Thanks for your code. – linojon Mar 13 '20 at 23:51
0

for Unity runtime permissions, i wrote this function to add the skip permissions dialog (like https://docs.unity3d.com/Manual/android-manifest.html)

e.g

<manifest>
    <application>
        <meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />

Here's the code:

internal void SetSkipPermissionsDialog()
{
    var manifest = SelectSingleNode("/manifest");
    var application = manifest.SelectSingleNode("application");
    XmlElement child = CreateElement("meta-data");
    application.AppendChild(child);
    XmlAttribute newAttribute = CreateAndroidAttribute("name", "unityplayer.SkipPermissionsDialog");
    child.Attributes.Append(newAttribute);
    newAttribute = CreateAndroidAttribute("value", "true");
    child.Attributes.Append(newAttribute);
}
linojon
  • 1,052
  • 1
  • 10
  • 19
0

To use a custom AndroidManifest file, Copy the AndroidManifest.xml file from <ProjectName>\Temp\StagingArea and put it on the Android folder <ProjectName>Assets\Plugins\Android. Create the Plugins and Android folder if not present in the project directory.

Codemaker2015
  • 12,190
  • 6
  • 97
  • 81