はじめに
Android初心者(自分含む)がつまづく点のひとつだと思われる、Android 6.0 Marshmallow以降でのパーミッション管理ですが、皆さんどのように書いておられるでしょうか。
すでに定型化できていればよいのですが、新しく実装する場合は今回紹介するPermissionsDispatcherが導入も簡単、かつ分かりやすく記述できて良さそうでした。
(今回例示するコードはJavaですが、最新の3系ではKotlinもフルサポートとのことです)
環境
Android Studio 3.0.1
JRE: 1.8.0_152-release-915-b08 x86_64
Mac OS X 10.13.3
導入
appモジュールのbuild.gradleを編集します。最新バージョンは公式のgithubを確認してください。
なお、上記のリンク先で書かれているexclude module: "support-v13"
のくだりは、Fragmentのサポートが不要な場合の記述なので、パーミッション周りの処理をFragmentでも行いたい場合は下記でOKです。
1 2 3 4 5 6 | dependencies{ ... compile'com.github.hotchemi:permissionsdispatcher:3.1.0' annotationProcessor'com.github.hotchemi:permissionsdispatcher-processor:3.1.0' } |
Activityでの実装例
アプリ内でカメラ機能のパーミッションが必要なケースを想定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | package... import... importpermissions.dispatcher.*; @RuntimePermissions// 必須 publicclassMainActivity extendsAppCompatActivity implementsView.OnClickListener{ @Override protectedvoidonCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.use_camera).setOnClickListener(this); } @Override publicvoidonClick(View view){ MainActivityPermissionsDispatcher.useCameraWithPermissionCheck(this); } @Override publicvoidonRequestPermissionsResult(intrequestCode,@NonNullString[]permissions,@NonNullint[]grantResults){ super.onRequestPermissionsResult(requestCode,permissions,grantResults); // 自動生成されるクラスにパーミッション管理を移譲する MainActivityPermissionsDispatcher.onRequestPermissionsResult(this,requestCode,grantResults); } @NeedsPermission(Manifest.permission.CAMERA)// 必須 publicvoiduseCamera(){ // カメラを使う処理を記述する Camera camera=null; try{ camera=Camera.open(); }catch(Exceptione){ e.printStackTrace(); } ... } @OnPermissionDenied(Manifest.permission.CAMERA)// オプション publicvoidonCameraDenied(){ Toast.makeText(this,"カメラ機能を利用できません。",Toast.LENGTH_LONG).show(); } @OnShowRationale(Manifest.permission.CAMERA)// オプション publicvoidshowRationaleForCamera(PermissionRequest request){ showRationaleDialog("カメラ機能を利用するためには、権限の許可が必要です。",request); } @OnNeverAskAgain(Manifest.permission.CAMERA)// オプション publicvoidifNeverAskAgain(){ Toast.makeText(this,"設定画面からカメラ機能の利用を許可してください。",Toast.LENGTH_LONG).show(); } privatevoidshowRationaleDialog(Stringmsg,finalPermissionRequest request){ newAlertDialog.Builder(this) .setPositiveButton(R.string.ok,newDialogInterface.OnClickListener(){ @Override publicvoidonClick(@NonNullDialogInterface dialog,intwhich){ request.proceed(); } }) .setNegativeButton(R.string.no,newDialogInterface.OnClickListener(){ @Override publicvoidonClick(@NonNullDialogInterface dialog,intwhich){ request.cancel(); } }) .setCancelable(false) .setMessage(msg) .show(); } } |
@RuntimePermissions(必須)
PermissionsDispatcherが動作するために必要なアノテーションです。
ActivityまたはFragmentのクラス宣言に付けます。
1 2 3 | @RuntimePermissions publicclassMainActivity extendsAppCompatActivity implementsView.OnClickListener{ ... |
PermissionsDispatcherに処理を移譲する
アノテーションを記述することによって自動生成されるクラスに、パーミッションの取り回しを移譲します。
最初はMainActivityPermissionsDispatcherが解決できず赤文字になるかもしれませんが、その場合はBuild
>Rebuild Project
を実行してください。
1 2 3 4 5 6 7 8 9 10 11 | @Override publicvoidonClick(View view){ MainActivityPermissionsDispatcher.useCameraWithPermissionCheck(this); } @Override publicvoidonRequestPermissionsResult(intrequestCode,@NonNullString[]permissions,@NonNullint[]grantResults){ super.onRequestPermissionsResult(requestCode,permissions,grantResults); // 自動生成されるクラスにパーミッション管理を移譲する MainActivityPermissionsDispatcher.onRequestPermissionsResult(this,requestCode,grantResults); } |
@NeedsPermission(必須)
パーミッションを要求するダイアログで「許可」を選択した時、あるいは既に許可されている場合の処理です。
(カメラの場合であればCamera.open()
など、パーミッションが無いとExceptionが発生するようなコードです)
1 2 3 4 5 6 7 8 9 10 | @NeedsPermission(Manifest.permission.CAMERA) publicvoiduseCamera(){ // カメラを使う処理を記述する Camera camera=null; try{ camera=Camera.open(); }catch(Exceptione){ e.printStackTrace(); } } |
ここまでは必ず記述することになる正常系のコードです。
以降はパーミッションの要求が拒否された場合の処理になります。
(optionalですが、実装しないということはあまり無いと思います)
@OnPermissionDenied(オプション)
「許可しない」が選択された時の処理です。
1 2 3 4 | @OnPermissionDenied(Manifest.permission.CAMERA) publicvoidonCameraDenied(){ Toast.makeText(this,"カメラ機能を利用できません。",Toast.LENGTH_LONG).show(); } |
@OnShowRationale(オプション)
パーミッション要求が拒否されている場合に、許可が必要な理由を表示します。request.proceed()
でパーミッション要求のダイアログが再表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @OnShowRationale(Manifest.permission.CAMERA) publicvoidshowRationaleForCamera(PermissionRequest request){ showRationaleDialog("カメラ機能を利用するためには、権限の許可が必要です。",request); } privatevoidshowRationaleDialog(Stringmsg,finalPermissionRequest request){ newAlertDialog.Builder(this) .setPositiveButton(R.string.ok,newDialogInterface.OnClickListener(){ @Override publicvoidonClick(@NonNullDialogInterface dialog,intwhich){ request.proceed(); } }) .setNegativeButton(R.string.no,newDialogInterface.OnClickListener(){ @Override publicvoidonClick(@NonNullDialogInterface dialog,intwhich){ request.cancel(); } }) .setCancelable(false) .setMessage(msg) .show(); } |
@OnNeverAskAgain(オプション)
パーミッション要求が一度拒否されると、二回目以降は「今後表示しない」というチェックボックスがダイアログに表示されるようになります。これをチェックして「許可しない」を選択すると、以降はアプリ内で権限を取得できなくなります。
この状態になると、アプリ内でできることは無くなりますので、ユーザーに手動で設定画面から許可してもらうよう促すしかありません。
設定画面への遷移処理をIntentで実装するケースもあると思います。
1 2 3 4 | @OnNeverAskAgain(Manifest.permission.CAMERA) publicvoidifNeverAskAgain(){ Toast.makeText(this,"設定画面からカメラ機能の利用を許可してください。",Toast.LENGTH_LONG).show(); } |
さいごに
いかがでしたでしょうか。
Marshmallow以前・以後をまたいでAndroidに関わっている方であれば、パーミッション周りについてはご自身の実装パターンをお持ちかもしれませんが、アノテーションを使ってスッキリ記述できるのは中々良いのではないでしょうか。
また、そうでない方にとっても「何を実装しなければいけないのか」「何をユーザーに伝える必要があるのか」が、コードを読むだけで理解しやすく、おすすめできると思います。