Android もセキュリティーを厳しくしているため、特定の機能はユーザーからの明確な許可を得ないと怒られます。
元々インストール時に許可をユーザーに求めていましたが、よりわかりやすく、また後から拒否することもできるようにしたのがこの Runtime Permission です。
Runtime Permission
これはAndroid 6.0(API 23)からの機能なので、それ以前の機種や対応アプリとは挙動が異なります。ただ、もう古い機種で少なくなっているとは思います。
Runtime Permission に対応しなくてもいいケースは、
- そもそもPermissionを入れていないアプリ
- より緩い Normal Permission は除外される
- 対応しないといけないのはDangerous Permission
マニフェスに、例えばWRITE_EXTERNAL_STORAGEを宣言している場合は対応が必要です。
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="your.package.name"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- other permissions go here --> <application ...> ... </application> </manifest> |
Dangerous Permission
以下が Dangerous Permission のグループとそれぞれ個別のPermissionです。
Dangerous Permission | |
Permission Group | Permissions |
CALENDAR | READ_CALENDAR WRITE_CALENDAR |
CAMERA | CAMERA |
CONTACTS | READ_CONTACTS WRITE_CONTACTS GET_ACCOUNTS |
LOCATION | ACCESS_FINE_LOCATION ACCESS_COARSE_LOCATION |
MICROPHONE | RECORD_AUDIO |
PHONE | READ_PHONE_STATE CALL_PHONE READ_CALL_LOG WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS |
SENSORS | BODY_SENSORS |
SMS | SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS |
STORAGE | READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE |
Ref:システム パーミッション | Android Developers
Permission at Runtimeに対応した実装
これらPermissionはインストール時は許可されていない状態です。
また、一旦許可してもその後ユーザーがそれを拒否させている場合もあるので、その機能を呼ばれた時に毎回確認します。
実装としては、起動後に許可を確認して拒否されていれば許可を求めるようにします
- APIが23以上か否か
- False: そのまま処理実行
- 許可しているか否か、checkSelfPermission(…)
- 許可していない場合や、初回起動のケースは次に行く
- 初めは許可していたが、途中で拒否をユーザーが選択するケースもあり得ることに注意
- 許可しているケースは処理に移る
- 許可していない場合や、初回起動のケースは次に行く
- Permissionが何に使われるか説明して許可を求める
- 拒否された場合はこの機能については動作しないが他の処理をする
- 例えばゲームアプリで得点をネットにアップするとき、ユーザーの位置情報もアップしたいという要請に対して拒否しても、ゲームは継続されるということです。
- 許可されて処理の実行
- 拒否された場合はこの機能については動作しないが他の処理をする
例として、GPSでACCESS_FINE_LOCATIONを使う場合を想定してみます
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="your.package.name"> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <application ... </application> </manifest> |
targetSdkVersionが27ですが
minSdkVersin を15としているので
Android6 とそれ以外のケース分けが必要となります
build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 |
apply plugin: 'com.android.application' android { compileSdkVersion 29 defaultConfig { applicationId "your.package.name" minSdkVersion 21 targetSdkVersion 29 ... } ... } ... |
1. APIが23以上か否か
APIレベルの確認をして23以上でパーミッションの確認をします。
(それ以下はすでにインストール時に許可されているはず)
1 2 3 4 5 6 7 8 |
// Android 6, API 23以上でパーミッシンの確認 if(Build.VERSION.SDK_INT >= 23){ checkPermission(); } else{ // GPSの測位を始める locationActivity(); } |
2. 許可しているか否か、checkSelfPermission()
パーミッションが既に許可されている場合(以前にユーザーによって許可された)
許可されていない場合(初回起動時あるいはユーザーによってこの機能は拒否されている)
アプリの機能以外はユーザーが使っていることもあり得ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 位置情報許可の確認 public void checkPermission() { // 既に許可している if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){ // GPSの測位を始める locationActivity(); } // 拒否していた場合 else{ requestLocationPermission(); } } |
3. Permissionが何に使われるか説明して許可を求める
許可されていない場合は許可を求める
この機能が許可されないと〇〇ができないなどユーザーに理由を示して許可を求める
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 許可を求める private void requestLocationPermission() { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERMISSION); } else { Toast toast = Toast.makeText(this, "許可されないとアプリが実行できません", Toast.LENGTH_SHORT); toast.show(); } } |
許可を求めるダイアログが表示されて、それにユーザーからの答えを受け取ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// 結果の受け取り @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_PERMISSION) { // 使用が許可された if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // GPSの測位を始める locationActivity(); } else { // それでも拒否された時の対応 Toast toast = Toast.makeText(this, "これ以上なにもできません", Toast.LENGTH_SHORT); toast.show(); } } |
実際にこれを実装したケースは以下にあります。
ユーザーによる許可
実機からユーザーの許可・拒否は設定で可能です。
「設定」「アプリと通知」から確認してみると、
「権限マネージャー」「位置情報」を見ると該当のアプリとして
TestGPSがあります。
アプリの位置情報権限が現在「許可しない」になっているのがわかります。これをユーザーが「アプリの使用中のみ許可」に変えたり、また「許可しない」に変更ができるわけです。
実装したアプリでのケース
上記の内容を実装したTestGPSでユーザーの許可・拒否についてどのようになるのか確認してみましょう。
初回起動時はこのような2択になります。
- アプリの使用中のみ許可
- 許可すればその後変更なければそのまま許可状態
- 許可しない
許可しない状態で再度このアプリの位置情報を使おうとすると、ちょっとイラっとしたダイアログになります。
- 許可しない(次回から表示しない)
これを選択するとこれ以上許可を求めるダイアログは表示されません
アプリによっては、許可されていないために機能が使えないとユーザーに理解してもらわないとバグっていると思われてしまうかもしれません。
また、ユーザーが理解していない可能性がある場合は説明することも必要です。
References:
アプリの権限をリクエストする | Android Developers
システム パーミッション | Android Developers