【Unity】UnityでAndroidのネイティブGUIを出したい

# はじめに
どうも、Unity DOTSの記事を上げまくっていた人です。
今日はどちらかというと、本来主としてやっていたスマホアプリ開発のお話です。
## 動機
最近、小学校から愛用していた目覚ましが壊れたので目覚ましアプリを作ろうと思いました。
ただ、Androidのネイティブの時刻設定はすごく好きだったのでそれを使いたいということで、Unityからネイティブを呼び出してみました。

Flutterだろ！とかネイティブで書けよ！っていうのはその通りです。
Unityが好きだしUnityの勉強がてらなんです許して

# 今回すること

https://twitter.com/KaidoAtsuya/status/1639520278240952321

これです。
## 環境
Unity 2021.3.20.f1
Android Studio Dolphin 2021.3.1 Patch1

# 最初にaarを作る
## aarとは
アンドロイドのライブラリの形式らしいです。Androidは詳しくないのでわかんないです。
## aarの作り方
詳しくは下記のQiitaをご覧ください。私も参考にさせていただきました。ありがとうございます。

https://qiita.com/atsutama/items/28744b1a29e79184c3fc

## 今回のソースコード
さて、時刻設定を出すソースコードはこちらです。
また、KotlinだとUnity上で動かすことができなかったです。大人しくJavaを使う運命かもしれません。
Kotlinの記法好きなんですけどね。少ししか触ってませんが。
```java
package com.prashalt.education.mathunialarm;

import static java.lang.String.valueOf;

import android.app.AlertDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.widget.TimePicker;

import com.unity3d.player.UnityPlayer;

public class AndroidNativeDialog{
    static public void showNativeDialog(Context context, String title, String message) {
        new AlertDialog.Builder(context)
                .setTitle(title)
                .setMessage(message)
                .setPositiveButton("はい", null)
                .setNegativeButton("いいえ", null)
                .show();
    }

    public static void showTimerDialog(Context context) {
        TimePickerDialog dialog = new TimePickerDialog(context, TimerDialogCallback(), 0, 0, true);
        dialog.show();
    }
    public static TimePickerDialog.OnTimeSetListener TimerDialogCallback() {
        return new TimePickerDialog.OnTimeSetListener() {
            @Override
            public void onTimeSet(TimePicker timePicker, int hour, int min) {
                UnityPlayer.UnitySendMessage("listenerObj", "TimeSetCallback", valueOf(hour) + ":" +  valueOf(min));
            }
        };
    }
}
```
このコードは、先ほど紹介したプログラムに少し手を加えたものになります。
メソッドである`TimerDialogCallback`は中身を別ファイルにしたり少し工夫をしたほうがきれいになりますが、まぁいいでしょう。
ひとつひとつ説明していきます（オリジナル部分のみ）
### TimePickerDialog
これはAndroidのAPIです。
#### AndroidのDialogについて
※この部分は雰囲気で書いてます。間違っていたらごめんなさい。
AndroidのDialogは時刻設定であってもほかのテキストの入力であっても通常は`AlertDialog`クラスを使って作成するようです。
ただ、今回のTimePickerDialogクラスはGoogleが親切に用意してくれた時刻設定のDialog専用のクラスです。（たぶん）
#### 引数
#### 1 Context
これはUnityから指定します。詳しくは先述のQiitaをご覧ください。
#### 2 Callback関数
"OK"ボタン（PositiveButton）が押された時の関数です。今回はUnityに結果を送信する関数を指定しています。
#### 3, 4 時間 / 分の初期値
#### 5 24時間表示かどうか

## dialog.show()
これで表示することができます。変数宣言に`var`を使ってないのは使ってるJavaのバージョンが古いからです。
深い意味はありません。

## Callback関数「TimerDialogCallback」
ここはもっときれいな書き方があります。ただ、めんどくさかったので許してください。
戻り値である、OnTimeSetメソッドは戻り値の`TimePickerDialog.OnTimeSetListener`にインターフェースとして実装されておりユーザーが入力した時間が取得できます。

## UnityPlayer.UnitySendMessage
これがUnity独自の部分ですね。UnityのC#に値を返すメソッドになります。
### 引数
#### 1 オブジェクト名
Unityにおける結果を受け取るオブジェクトの名前です。
唯一の名前である必要があるらしいです。
#### 2 メソッド名
オブジェクトにアタッチしてあるMonoBehaviourを継承するクラスに定義されているメソッドの名前です。
これが呼ばれます。
#### 3 メッセージ
これが結果になります。基本的にstringでしか返せません。シリアライズなどをすることでそれ以外も返せるらしいです。

## C#のスクリプト
解説するまでもないですが、一応おいておきます。
ほぼ、先述のQiita通りです。

**NativeTest.cs**
`ShowNativeDialog()`をボタンから呼んでいます。
```C#
using UnityEngine;

public class NativeTest : MonoBehaviour
{
    public void ShowNativeDialog()
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        AndroidJavaClass nativeDialog = new AndroidJavaClass ("com.prashalt.education.mathunialarm.AndroidNativeDialog");
        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject context  = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

        context.Call ("runOnUiThread", new AndroidJavaRunnable(() => {
            nativeDialog.CallStatic (
                "showTimerDialog",
                context
            );
        }));
#elif UNITY_EDITOR
        UnityEngine.Debug.Log("On Click!");
#endif
    }
}
```
**ListenerTest.cs**
`TimeSetCallback()`をUnityPlayer.UnitySendMessageで呼んでいます。
```C# 
using UnityEngine;

public class ListenerTest : MonoBehaviour
{
    public void TimeSetCallback(string message)
    {
        Debug.Log(message);
    }
}
```
# おわりに
いかがでしたでしょうか。
時刻設定のダイアログ以外を出したいとなると、もはやAndroidの世界です。
今回はUnityとの連携という側面で記事を書いていたのでそれは誰かに任せましょう。

もしかしたら、結果をシリアライズする方法の記事も上げるかもしれません。
ただ、今はシリアライズってなんだよ！カタカナ使うな！ぐらいの知識ですけどね笑
SerializeFieldぐらいでしかシリアライズ使ったことないです。

それではまたっ！