Android
Unity

UnityでAndroidのランタイムパーミッションに対応する

UnityでAndroid 6.0以降のランタイムパーミッションの対応をしたかったので、やってみました。ランタイムパーミッションを使わないとAndroid 6.0以降ではWrite PermissionをExternal (SDCard)にしていても外部ストレージにアクセスできません。普通のAndroidアプリではUnityが起動時に自動的にランタイムパーミッションの処理をしてくれますが、DayDreamアプリだとこの処理をしてくれなかったので(2017.3時点)自前で実装してみました。

このためだけにプラグインを作るのもなんだかなーと思ったので、UnityのC#だけで必要最低限の処理を書いてみました。

まず、以下のようなクラスを用意します。これはAndroidのネイティブAPIのラッパーです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RuntimePermissionHelper
{
  private RuntimePermissionHelper() { }

  // 実行中のActivityインスタンスを取得する
  private static AndroidJavaObject GetActivity()
  {
    using (var UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    {
      return UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
    }
  }

  // Android M以上かどうか
  private static bool IsAndroidMOrGreater()
  {
    using (var VERSION = new AndroidJavaClass("android.os.Build$VERSION"))
    {
      return VERSION.GetStatic<int>("SDK_INT") >= 23;
    }
  }

  // パーミッションを持っているかどうかを調べる
  public static bool HasPermission(string permission)
  {
    if (IsAndroidMOrGreater())
    {
      using (var activity = GetActivity())
      {
        return activity.Call<int>("checkSelfPermission", permission) == 0;
      }
    }

    return true;
  }

  // パーミッションが必要であることを説明するUIを出す必要があるか
  public static bool ShouldShowRequestPermissionRationale(string permission)
  {
    if (IsAndroidMOrGreater())
    {
      using (var activity = GetActivity())
      {
        return activity.Call<bool>("shouldShowRequestPermissionRationale", permission);
      }
    }

    return false;
  }

  // パーミッション許可ダイアログを表示する
  public static void RequestPermission(string[] permissiions)
  {
    if (IsAndroidMOrGreater())
    {
      using (var activity = GetActivity())
      {
        activity.Call("requestPermissions", permissiions, 0);
      }
    }
  }
}

usingがあった方がいいのかない方がいいのかわからなかったのでとりあえず付けてます)

あとはこのクラスを使ってパーミッションのチェックとダイアログの表示を行います。

使用例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RuntimePermission : MonoBehaviour
{
  private string permission = "android.permission.WRITE_EXTERNAL_STORAGE";
  private bool permissionRequested = false;

  void Start()
  {
    if (!RuntimePermissionHelper.HasPermission(permission))
    {
      if (RuntimePermissionHelper.ShouldShowRequestPermissionRationale(permission))
      {
        // パーミッションを要求する意味を説明するUIを表示
      }
      else
      {
        // パーミッションをリクエスト
        RuntimePermissionHelper.RequestPermission(new string[] { permission });
        permissionRequested = true;
      }
    }
  }

  // パーミッションダイアログから戻ってきたときなどに呼ばれる
  void OnApplicationPause(bool pauseStatus)
  {
    // ポーズからの復帰時かつパーミッションリクエストの直後の場合
    if (!pauseStatus && permissionRequested)
    {
      // パーミッションを持っているかどうか
      if (RuntimePermissionHelper.HasPermission(permission))
      {
        Debug.Log("パーミッションリクエスト成功!");
        permissionRequested = false;
      }
    }
  }
}

本来はランタイムパーミッションの結果を取得するためにはActivityを継承してonRequestPermissionsResultをオーバーライドする必要がありますが、そのためにはいろいろと設定が必要でAndroid Studioも併用する必要があり面倒だったので、簡易的にOnApplicationPauseで判断することにしました。OnApplicationPauseはパーミッションダイアログから復帰した後以外にも単にアプリがバックグラウンドに移動したり復帰したりした時にも呼ばれるため、パーミッションリクエストを行った直後であることを表すフラグpermissionRequestedを用意しています。

この方法であればandroid.permission.WRITE_EXTERNAL_STORAGE以外のパーミッションもリクエストすることができますが、パーミッションをリクエストするためにはAndroidManifest.xmlに定義されている必要があるので注意してください。