UnityでAndroid向けのネイティブプラグインを作る

これは何

UnityではAndroidの音量を操作するためのAPIが存在しないので、ネイティブプラグインを作る必要がある。

AndroidStudioでとりあえずサンプルを作ってみる

Androidのコードをコンパイルするために、一番手っ取り早いのはAndroid Studioを使い、モジュール(aar)を作ることである。
Kotlinで書く場合、Static Methodを作れない(作れはするものの、@JvmStaticをUnityが理解出来ずに挫折)ので、Javaでプロジェクトを作る(APIレベルは念の為Android12とかにしておく)

プロジェクトが出来たら「File」->「New」->「New Module」でモジュールを作る(作り終えたら「File」->「Project Structure」のModulesからappを削除しておく)
※ここでTemplateがデフォルトでPhone&Tabletになっているので、Android Libraryを選択すること
build.gradle.ktsのdependencyに2行追加

モジュールのlibsディレクトリにUnityEditorに含まれているclasses.jarを移植しておく
(コンパイルエラーを回避するためのものでArtifactには含まれないようにする。モジュールからUnityにSendMessageしたりしないなら不要)

    public static String getStreamVolumes(Context ctx) {
        AudioManager audioManager = (AudioManager) ctx.getSystemService(AUDIO_SERVICE);
        Map<String, Integer> volumeMap = new HashMap<String, Integer>();
        volumeMap.put("accessibility", audioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY));
        volumeMap.put("alarm", audioManager.getStreamVolume(AudioManager.STREAM_ALARM));
        volumeMap.put("music", audioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
        volumeMap.put("dtmf", audioManager.getStreamVolume(AudioManager.STREAM_DTMF));
        volumeMap.put("notification", audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION));
        volumeMap.put("ring", audioManager.getStreamVolume(AudioManager.STREAM_RING));
        volumeMap.put("system", audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM));
        volumeMap.put("voiceCall", audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL));

        Gson gson = new Gson();
        String ret = gson.toJson(volumeMap);
        return ret;
    }

こんな感じのコードを書いて、Androidの現在のボリュームをJSONで返却するような実装を行う
メニュー「Build」->「Make Module ‘モジュール名’」でビルドを実行する
実行が完了するとライブラリルートから「./build/outputs/aar」以下にライブラリのパッケージが出来上がっている(Build Variantはお好みで)

※Gsonは匿名クラスをnull化するので、Mapの初期化(new HashMap<>(){{put(key, value)}}みたいなやつ)で匿名クラス化してしまわないように注意。

Unityでのインポート

/Assets/Plugins/Android以下に、作成したaarファイルと、今回サンプルのプログラムで依存するGsonのjarを配置。

        AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject activity = jc.GetStatic<AndroidJavaObject>("currentActivity");
        AndroidJavaObject ctx = activity.Call<AndroidJavaObject>("getApplicationContext");
        var audioHelper = new AndroidJavaClass("xxxxxxxxxxxxxxx.AndroidAudioHelper"); <-- rewrite me
        var androidRes = audioHelper.CallStatic<string>("getStreamVolumes", ctx);
        Debug.LogError($"AndroidRes: {androidRes}");

こんな感じでApplicationContextをネイティブのStaticMethodに渡して色々動かすことが出来る。

aarのデコンパイル

実際触って何かしら謎挙動に遭遇した時、aarのデコンパイルをしたくなる。
一番手っ取り早いと思ったのがjadxで、Macならば「brew install jadx」からの「jadx-gui」でGUIのデコンパイラが起動し、aarファイルをドラッグするだけで一発でデコンパイルしてくれる。