はじめに
本稿では PermissionsDispatcher による権限管理に関して、簡単な説明とサンプルアプリの提示を行います。
Android の権限に関する包括的な情報
以下の情報を参照のこと:
-
App Permissions
- Android の権限に関する包括的な情報
PermissionsDispatcher
◆ 概要
PermissionsDispatcher は、実行時の権限を扱うためのシンプルなアノテーションベースの API を提供します。
このライブラリは、権限が許可されているかどうかのチェック用コードを大量に記述することに伴う負担を軽減し、クリーンで安全なコードの保持に寄与します。
◆ 公式サイト
サンプルアプリ
◆ 概要
カメラ起動ボタンを押下すると、カメラの権限の必要な機能が呼び出されるサンプルアプリです。
◆ AndroidManifest.xml
AndroidManifest.xml にて、権限の設定が必要になります。
<!--
権限の付与。
記述し忘れても、当該権限を利用している PermissionsDispatcher 関連コードは
ビルド時にも実行時にもエラーとならず、自動的に権限申請が却下扱いとなるので要注意。
-->
<uses-permission android:name="android.permission.CAMERA" />
◆アノテーション設定
☆ @RuntimePermissions
-
必須のアノテーションです。
-
Activity や Fragment に対して
@RuntimePermissions
アノテーションを付与することにより、PermissionsDispatcher による権限のハンドリングが有効になります。
@RuntimePermissions
class MainActivity : AppCompatActivity() {
}
☆ @NeedsPermission
-
必須のアノテーションです。
-
@NeedsPermission
アノテーションが付与されたメソッドに対しては、annotation processor によりメソッド名にWithPermissionCheck
が付与されたラッパーメソッドが自動的に生成されます。 -
開発者は、オリジナルのメソッドではなく、ラッパーメソッドを呼び出します。
-
@NeedsPermission
アノテーションが付与されたメソッドは、ラッパーメソッド経由で権限のリクエストが許可された直後に PermissionsDispatcher により自動的に呼び出されます。 -
private スコープは使用できません。
/**
* カメラの権限を必要とする任意のメソッド。
*/
@NeedsPermission(Manifest.permission.CAMERA)
fun showCamera() {
Toast.makeText(this, "showCamera()", Toast.LENGTH_SHORT).show()
}
☆ @OnShowRationale
-
@OnShowRationale
アノテーションが付与されたメソッドは、『今後表示しない』が設定されていない状態で権限のリクエストが発生した直後に PermissionsDispatcher により自動的に呼び出されます。 -
引数に、続行か中断を指示できる PermissionRequest を指定することができます。これを用いると、ダイアログに渡すことによりダイアログ側で続行か中断の指示を出すようなことが可能になります。
-
引数が指定されていない場合は、
proceed${NeedsPermissionMethodName}ProcessRequest
とcancel${NeedsPermissionMethodName}ProcessRequest
メソッドが@RuntimePermissions
アノテーションが付与された Activity や Fragment に生成され、これらのメソッドにより続行か中断の指示を出すことができます。 -
private スコープは使用できません。
/**
* 権限が必要な理由を表示する任意のメソッド。
*/
@OnShowRationale(Manifest.permission.CAMERA)
fun showRationaleForCamera(request: PermissionRequest) {
showRationaleDialog(request)
}
/**
* `@OnShowRationale` 経由で起動され、権限が必要な理由を表示して許可の是非を問うダイアログを表示するメソッド。
*/
private fun showRationaleDialog(request: PermissionRequest) {
AlertDialog.Builder(this)
.setPositiveButton("許可") { _, _ -> request.proceed() }
.setNegativeButton("拒否") { _, _ -> request.cancel() }
.setCancelable(false)
.setMessage("カメラの権限が必要です")
.show()
}
☆ @OnPermissionDenied
-
@OnPermissionDenied
アノテーションが付与されたメソッドは、『今後表示しない』が設定されていない状態で権限付与が拒否された場合に PermissionsDispatcher により自動的に呼び出されます。 -
private スコープは使用できません。
/**
* 権限が拒否された場合に呼び出される任意のメソッド
*/
@OnPermissionDenied(Manifest.permission.CAMERA)
fun onCameraPermissionDenied() {
Toast.makeText(this, "onCameraPermissionDenied()", Toast.LENGTH_SHORT).show()
}
☆ @OnNeverAskAgain
-
@OnNeverAskAgain
アノテーションが付与されたメソッドは、『今後表示しない』が設定された状態で権限付与が拒否された場合に PermissionsDispatcher により自動的に呼び出されます。 -
private スコープは使用できません。
/**
* 『今後表示しない』が選択された場合に呼び出される任意のメソッド
*/
@OnNeverAskAgain(Manifest.permission.CAMERA)
fun onCameraNeverAskAgain() {
Toast.makeText(this, "onCameraNeverAskAgain()", Toast.LENGTH_SHORT).show()
}
◆ 自動生成コードとのつなぎ込み
☆ ラッパーメソッド
@NeedsPermission
アノテーションの付与されたメソッドは、権限チェック込みで呼び出すためのラッパーメソッドが自動生成されます。
ラッパーメソッドの名称は、オリジナルのメソッド名の末尾に WithPermissionCheck
が付いたものになります。
/**
* 画面中央の『カメラ起動』ボタンが押された時に呼び出されるメソッド。
*/
private fun onShowCameraButtonClick() {
// showCamera() を呼ぶのではなく、自動生成されたラッパーメソッドの showCameraWithPermissionCheck() を呼んでいる点に注意。
showCameraWithPermissionCheck()
}
☆ onRequestPermissionsResult()
OnRequestPermissionsResultCallback#onRequestPermissionsResult() の呼び出しを、自動生成されたハンドリング用の extension function に委譲します。
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// 自動生成された権限ハンドリング用のコードに処理を委譲するための extension function を呼び出す。
onRequestPermissionsResult(requestCode, grantResults)
}
画面サンプル
◆ MainActivity
◆ showRationaleDialog()
権限が必要な理由を表示するためのダイアログです。
権限設定ダイアログに置き換わるものではなく、権限設定ダイアログの前に表示されるダイアログになります。
◆ 権限設定ダイアログ
システムにより自動的に表示されるダイアログです。
☆ 通常表示
☆ 『今後表示しない』チェック時
今後表示しないをチェックした際には、許可が選択できないようになっています。1
おわりに
本稿では PermissionsDispatcher による権限管理に関して、簡単な説明とサンプルアプリの提示を行いました。
サンプルの詳細については Appendix を参照してください。
Appendix
◆ サンプルアプリの GitHub リポジトリ
◆ 主要コード
◆ MainActivityソース
package com.objectfanatics.chrono0016
import android.Manifest
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import permissions.dispatcher.*
// ・必須のアノテーションです。
// ・Activity もしくは Fragment に対して @RuntimePermissions アノテーションを付与することにより、
// PermissionsDispatcher による権限のハンドリングが有効になります。
@RuntimePermissions
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<View>(R.id.show_camera_button).setOnClickListener {
onShowCameraButtonClick()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// 自動生成された権限ハンドリング用のコードに処理を委譲するための extension function を呼び出す。
onRequestPermissionsResult(requestCode, grantResults)
}
/**
* 画面中央の『カメラ起動』ボタンが押された時に呼び出されるメソッド。
*/
private fun onShowCameraButtonClick() {
// showCamera() を呼ぶのではなく、自動生成されたラッパーメソッドの showCameraWithPermissionCheck() を呼んでいる点に注意。
showCameraWithPermissionCheck()
}
/**
* カメラの権限を必要とする任意のメソッド。
*/
// ・必須のアノテーションです。
// ・`@NeedsPermission` アノテーションが付与されたメソッドに対しては、annotation processor により
// `WithPermissionCheck` が付与されたラッパーメソッドが自動的に生成され、開発者はそちらの
// ラッパーメソッドを呼び出します。
// ・ラッパーメソッド経由で権限のリクエストが許可された直後に PermissionsDispatcher により自動的に呼び出されます。
// ・private スコープは使用できません。
@NeedsPermission(Manifest.permission.CAMERA)
fun showCamera() {
Toast.makeText(this, "showCamera()", Toast.LENGTH_SHORT).show()
}
/**
* 権限が必要な理由を表示する任意のメソッド。
*/
// ・`@OnShowRationale` アノテーションが付与されたメソッドは、『今後表示しない』が設定されていない状態で
// 権限のリクエストが発生した直後に PermissionsDispatcher により自動的に呼び出されます。
// ・引数に、続行か中断を指示できる PermissionRequest を指定することができます。これを用いると、
// ダイアログに渡すことによりダイアログ側で続行か中断の指示を出すようなことが可能になります。
// ・引数が指定されていない場合は、proceed${NeedsPermissionMethodName}ProcessRequest と cancel${NeedsPermissionMethodName}ProcessRequest
// メソッドが @RuntimePermissions アノテーションが付与された Activity や Fragment に生成され、
// これらのメソッドにより続行か中断の指示を出すことができます。
// ・private スコープは使用できません。
@OnShowRationale(Manifest.permission.CAMERA)
fun showRationaleForCamera(request: PermissionRequest) {
showRationaleDialog(request)
}
/**
* 権限が拒否された場合に呼び出される任意のメソッド
*/
// ・`@OnPermissionDenied` アノテーションが付与されたメソッドは、『今後表示しない』が設定されていない状態で
// 権限付与が拒否された場合に PermissionsDispatcher により自動的に呼び出されます。
// ・private スコープは使用できません。
@OnPermissionDenied(Manifest.permission.CAMERA)
fun onCameraPermissionDenied() {
Toast.makeText(this, "onCameraPermissionDenied()", Toast.LENGTH_SHORT).show()
}
/**
* 『今後表示しない』が選択された場合に呼び出される任意のメソッド
*/
// `@OnNeverAskAgain` アノテーションが付与されたメソッドは、『今後表示しない』が設定された状態で
// 権限付与が拒否された場合に PermissionsDispatcher により自動的に呼び出されます。
@OnNeverAskAgain(Manifest.permission.CAMERA)
fun onCameraNeverAskAgain() {
Toast.makeText(this, "onCameraNeverAskAgain()", Toast.LENGTH_SHORT).show()
}
/**
* `@OnShowRationale` 経由で起動され、権限が必要な理由を表示して許可の是非を問うダイアログを表示するメソッド。
*/
private fun showRationaleDialog(request: PermissionRequest) {
AlertDialog.Builder(this)
.setPositiveButton("許可") { _, _ -> request.proceed() }
.setNegativeButton("拒否") { _, _ -> request.cancel() }
.setCancelable(false)
.setMessage("カメラの権限が必要です")
.show()
}
}
-
許可した際には今後表示されないので、当然と言えば当然ですね。 ↩