Unity上のiOS/Androidで位置情報を取得とiOS/Androidのパーミッションまわりについて試して見たことを記載します。
Unity公式の位置情報取得のサンプルコード
Unity公式の位置情報取得のサンプルコードのコメントを訳したものを下記に記載します。
このコードでも位置情報を取得できますが、Unityの位置情報の取得を管理するクラスLocationServiceがシンプルすぎて、iOS/Androidの位置情報取得の可否の状態によってどういう挙動をするのかがドキュメントからは不明瞭なので、本記事ではそのあたりを実機を動かして確認していきます。
using UnityEngine;
using System.Collections;
public class TestLocationService : MonoBehaviour
{
IEnumerator Start()
{
// 最初に、ユーザーがロケーションサービスを有効にしているかを確認する。無効の場合は終了する
if (!Input.location.isEnabledByUser)
yield break;
// 位置を取得する前にロケーションサービスを開始する
Input.location.Start();
// 初期化が終了するまで待つ
int maxWait = 20; // タイムアウトは20秒
while (Input.location.status == LocationServiceStatus.Initializing && maxWait > 0)
{
yield return new WaitForSeconds(1); // 1秒待つ
maxWait--;
}
// サービスの開始がタイムアウトしたら(20秒以内に起動しなかったら)、終了
if (maxWait < 1)
{
print("Timed out");
yield break;
}
// サービスの開始に失敗したら終了
if (Input.location.status == LocationServiceStatus.Failed)
{
print("Unable to determine device location");
yield break;
}
else
{
// アクセスの許可と位置情報の取得に成功
print("Location: " + Input.location.lastData.latitude + " "
+ Input.location.lastData.longitude + " "
+ Input.location.lastData.altitude + " "
+ Input.location.lastData.horizontalAccuracy + " "
+ Input.location.lastData.timestamp);
}
// 位置の更新を継続的に取得する必要がない場合はサービスを停止する
Input.location.Stop();
}
}
動作環境
- Unity 2018.4f8 Mac版
- XCode 11.0
各OSの位置情報取得の許可の状態について
iOSとAndroidのOSごとの位置情報取得の許可の状態について下記で記載します。
UnityではこのあたりがLocationServiceによって抽象化されてしまっています。
iOS
CLAuthorizationStatusに記載されているように、下記の5状態が存在します。
- notDetermined - ユーザーは未選択
- restricted - 許可されていない(ユーザーは特に拒否していないが何らかの理由で使用できない場合)
- denied - ユーザーは拒否
- authorizedAlways - 常に許可
- authorizedWhenInUse - アプリの使用中のみ許可
notDetermined
ユーザーはアプリで位置情報サービスを使用できるかどうかを選択していない。
位置情報取得の確認ダイアログをまだ表示したことがないのがこの状態。
restricted
アプリは位置情報サービスの使用を許可されていない。
ユーザーは位置情報サービスの使用を特に拒否していないが、何らかの理由で使用できない場合にこの状態になるはず。
「設定 > プライバシー > 位置情報サービス」で位置情報サービスをオフにした場合はこの状態(iOS 13.1.2で確認)。
denied
ユーザーがアプリの位置情報サービスの使用を拒否したか、設定でグローバルに無効化されている。
位置情報の取得確認ダイアログで拒否を選択したか、「設定 > プライバシー > 位置情報サービス」で「なし」を選択したかのどちらかがこの状態。
authorizedAlways
ユーザーは、いつでも位置情報サービスを開始することをアプリに許可した。
位置情報の取得確認ダイアログで「常に」を選択するとこの状態になる。アプリがバックグラウンドにいてもアプリは位置情報が取得可能。
authorizedWhenInUse
ユーザーは、アプリが使用中に位置情報サービスを開始することを承認した。
位置情報の取得確認ダイアログで「使用中のみ」を選択するとこの状態になる。アプリがフォアグラウンドの状態でのみアプリは位置情報が取得可能。
Android
下記の3状態が存在する。
- 未選択・1度だけ拒否
- 許可済み
- 拒否
未選択・1度だけ拒否
ユーザーはアプリで位置情報を使用できるかどうかを選択していない。
または、アプリで位置情報を使用を許可していない。
位置情報取得の確認ダイアログをまだ表示したことがないのがこの状態。
または、位置情報取得の確認ダイアログで拒否を選択したのがこの状態。
- Androidでは、この状態で PermissionChecker.checkSelfPermission を呼ぶと、 PackageManager.PERMISSION_DENIED(未許可)が返ってきます。
- Androidでは、この状態で Activity.requestPermissions を呼ぶと、位置情報取得の確認ダイアログが表示されます。
許可済み
ユーザーは位置情報サービス使用することをアプリに許可した。
位置情報取得の確認ダイアログで許可を選択したのがこの状態。
- Androidでは、この状態で PermissionChecker.checkSelfPermission を呼ぶと、 PackageManager.PERMISSION_GRANTED (許可済み)が返ってきます。
拒否
アプリで位置情報を使用を許可していない。
位置情報取得の確認ダイアログで「今後表示しない」を選択したのがこの状態。
- Androidでは、この状態で PermissionChecker.checkSelfPermission を呼ぶと、 PackageManager.PERMISSION_DENIED(未許可)が返ってきます。
- Androidでは、この状態で Activity.requestPermissions を呼ぶと、即座に Activity. onRequestPermissionsResult にコールバックが返ってきます。
Android 9での位置情報取得の確認ダイアログで「今後表示しない」のチェックが存在するもの
位置情報取得の許可ごとの挙動について
LocationService.isEnabledByUser
LocationService.isEnabledByUser
ユーザーが位置情報サービスを有効にするかどうか。
iOS
「設定 > プライバシー > 位置情報サービス」で位置情報サービスをオフにした場合にfalseが返る。
位置情報の許可の有無とは関係なさそう。
状態 | LocationService.isEnabledByUserの返値 |
---|---|
notDetermined | true |
restricted | false |
denied | true |
authorizedAlways | true |
authorizedWhenInUse | true |
Android
状態 | LocationService.isEnabledByUserの返値 |
---|---|
未選択・1度だけ拒否 | false |
許可済み | true |
拒否 | false |
LocationService.Start
LocationService.Start
位置情報サービスの更新を開始します。
LocationService.Start
を呼び出し後、LocationService.statusがLocationServiceStatus.Runningになったら、LocationService.lastDataより位置情報を取得できます。
iOS
状態 | LocationService.Start呼び出し後の動作 |
---|---|
notDetermined | 位置情報の取得の確認ダイアログが表示される。 |
restricted | "位置情報を利用できるようにするには、位置情報をサービスをオンにしてください"というダイアログが表示される。LocationService.statusはFailed になる。 |
denied | LocationService.statusはFailed になる。 |
authorizedAlways | LocationService.statusはRunning になる。 |
authorizedWhenInUse | LocationService.statusはRunning になる。 |
位置情報の取得の確認ダイアログで許可を選択した場合に、LocationService.status
はRunning
となる。
"位置情報を利用できるようにするには、位置情報をサービスをオンにしてください"のダイアログ
Android
状態 | LocationService.Start呼び出し後の動作 |
---|---|
未選択・1度だけ拒否 | 位置情報の取得の確認ダイアログが表示される。LocationService.statusはStopped のままで、許可を選択するとLocationService.isEnabledByUser はtrueになる。 |
許可済み | LocationService.statusはRunning になる。 |
拒否 | LocationService.statusはStopped になる。 |
位置情報の取得の確認ダイアログで許可を選択しても、LocationService.isEnabledByUser
はtrueになるがLocationService.status
の状態に変化はない。
位置情報を取得する場合はもう一度LocationService.Start
を呼ぶ必要がありそう。
LocationService.Stop
LocationService.Stop
位置情報サービスの更新を停止します。
iOS/Android
LocationService.status
がStopped
になります。
LocationService.lastData
の値が更新されなくなります。
まとめ
-
iOSは「設定 > プライバシー > 位置情報サービス」がオンかオフかは
LocationService.isEnabledByUser
を見れば分かる -
iOSはアプリの位置情報取得が許可されているかどうかはあらかじめ分からない。
LocationService.Start
を呼んだ挙動で判定するしかなさそう。 -
iOSアプリは位置情報取得の許可ダイアログが表示されるかどうかはあらかじめ分からない。
LocationService.Start
を呼んだときにOSにより位置情報取得の許可ダイアログが表示される。 -
Androidはアプリの位置情報取得が許可されているかどうかは
LocationService.isEnabledByUser
を見れば分かる -
Androidアプリは位置情報取得の許可が無い場合(
LocationService.isEnabledByUser
がfalseの場合)に、位置情報取得の許可ダイアログが表示されるかどうかはあらかじめ分からない。LocationService.Start
を呼んだときにOSにより位置情報取得の許可ダイアログが表示される。
位置情報が取得できるかどうかをiOSとAndroidの両方に対応しようとすると、LocationService.Start
を呼んだ上でLocationService.status
の値がどうなるかを監視するくらししかなさそうです。
コード