【Unity】ハードウェアアクセラレーションが有効にならない、AndroidManifestを書き換えても無効になってしまう問題の解決

## 問題の概要
AndroidManifestで有効にしたHardware accelerationが反映されない問題。
AndroidManifestでHardware accelerationのactivityをtrueにしても反映されない。

あまり日本語の記事が無かったので投稿。

### 予備知識
https://developer.android.com/guide/topics/graphics/hardware-accel
https://usedoor.jp/howto/web/code/android-dev-webview-scroll-kousokuka-hardwareaccelerated/
https://www.teradas.net/archives/12229/

Hardware accelerationがOFFになっているとHTML5のビデオ再生が使えなくなったり、WebViewのスクロールが重くなってガクガクになる。
ただこれをONにすることによってGPUレンダリングされることになるので、パフォーマンスが落ちたりするケースがある。(電池消費が激しくなったり)

## 問題の原因
Unity2018から?強制的に、Hardware accelerationがfalseに書き換わっているとのこと。
Unityの中の人がパフォーマンスが低下してしまうため、強制的に無効化するようにしたらしい。　(えぇ....)

そのことについてのフォーラムページ
https://forum.unity.com/threads/android-hardwareaccelerated-is-forced-false-in-all-activities.532786/?gq=NativeActivity

## どのように解決したか

> If you are absolutely sure you need the hardware acceleration, you can to the following:
> - Extract your native UI into a separate activity and enable hardware acceleration there (preferred way)
> - Modify the Unity manifest, for examply by utilizing IPostGenerateGradleAndroidProject interface (sample here: https://github.com/Over17/UnityAndroidManifestCallback)
> ※フォーラムから抜粋

Gradleビルドするときに呼ばれるインターフェース(`IPostGenerateGradleAndroidProject`)を実装して、そのコールバックにアクセラレーションを有効にするManifestを書き換えればOK。このインターフェースはAndroidManifestをUnityが強制的に書き換えた後に呼ばれるので、書き換えが可能になる。
BuildSystemが「Internal」だったら「Gradle」にすること。

このインターフェースを実装しているsampleがgithubにあるので、参考にしました。
[GitHub - Over17/UnityAndroidManifestCallback: Apply your changes to Android Manifest in Unity 2018.1+ without having to override the main manifest of the whole project](https://github.com/Over17/UnityAndroidManifestCallback)

以下のスクリプトを`Assets/Editor/`に入れる。

```csharp:ModifyUnityAndroidAppManifest.cs
public class ModifyUnityAndroidAppManifest : IPostGenerateGradleAndroidProject
{
    public readonly string ActivityName = "com.hoge.ui.UiActivity";
    public readonly string ThemeName = "@style/FloatingActivityTheme";

    public void OnPostGenerateGradleAndroidProject(string basePath)
    {
        var androidManifest = new AndroidManifest(GetManifestPath(basePath));

		Debug.LogFormat("**************OnPostGenerateGradleAndroidProject BasePath={0}, ManifestPath={1}**************", basePath, GetManifestPath(basePath));
		// 必要であれば
		//androidManifest.SetApplicationTheme(ThemeName);
		//androidManifest.SetStartingActivityName(ActivityName);

		// manifestを書き換えるハードウェアアクセラレーションを有効にする
		androidManifest.SetHardwareAccel(); 
        // Add your XML manipulation routines

        androidManifest.Save();
		Debug.LogFormat("**************OnPostGenerateGradleAndroidProject Done!**************");
	}

    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 SetHardwareAccel()
	{
		GetActivityWithLaunchIntent().Attributes.Append(CreateAndroidAttribute("hardwareAccelerated", "true"));
	}
}
```

## 備考
* VoxelBustersの中の人が上記のフォーラムページにて「いいね！良い情報をもらったので、修正できるよ！！」と言っているが今現在実装してなさそう。(2019/4/18)
* IPostGenerateGradleAndroidProject は2018.3からしか使えないらしい。 これ以外のバージョンはアップデートをするかパッケージを用意してAndroidプロジェクトにエクスポートしないといけない(面倒くさい)