PermissionsDispatcherをはずして、PermissionsDispatcher風に実装しよう。

## はじめに

- Androidのパーミッション処理を便利に実装してくれる[PermissionsDispatcher](https://github.com/permissions-dispatcher/PermissionsDispatcher)ってなかなか便利ですよね。
- 使っている方もいるかと思いますが、PermissionsDispatcherを外して、PermissionsDispatcher風に実装する方法を書いていこうと思います。

- ここでは`PermissionsDispatcher`の使い方についてはふれませんので、気になる方はQiitaの他の記事を読んでみると良いかと:neutral_face:

## PermissionsDispatcher風とは？

- `PermissionsDispatcher`では、メソッドに`Annotation`を付与し、kapt（kotlin-annotation-processing tools）を利用しコード生成を行います。

- `PermissionsDispatcher`には以下の`Annotation`が用意されています。

![スクリーンショット 2020-11-10 21.31.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495289/a484ff31-320b-2a22-1f6a-5b6a19851c87.png)
[annotations](https://github.com/permissions-dispatcher/PermissionsDispatcher#1-attach-annotations)

- この`Annotation`の処理と、[AndroidDevelopersGuilde:アプリの権限をリクエストする](https://developer.android.com/training/permissions/requesting?hl=ja) の内容をベースに実装を考えていきます。

## 実装イメージ

- Permissionの判定処理は、`androidx.activity:activity-ktx`と`androidx.fragment:fragment-ktx`を利用します。
- Permission判定は以下の処理をハンドリング出来るようにします。

|処理名|タイミング|
|---|---|
|onGranted|ユーザーが処理を許可した場合に呼び出されます。|
|onShowRationale|ユーザーが処理を一度拒否した後に、リクエストされた際に呼び出されます。|
|onDenied|ユーザーが処理を拒否した場合際に呼び出されます。|

### OSS

`androidx.activity:activity-ktx`を利用します。

``` build.gradle
dependencies {
    implementation 'androidx.activity:activity-ktx:1.2.0-beta01'
}
```

### Permission判定処理

- 判定処理は`ActivityResultContracts.RequestPermission()`を元に、判定処理を実装します。
- `ActivityResultContracts.RequestPermission()`はActivity間パーミッション判定を実装してくれています。

### Permissionの状態管理

- パーミッション判定と、処理のハンドリングを以下のクラスで制御します。

``` PermissionChecker.kt

class PermissionChecker(
        private val activity: ComponentActivity,
        private val permission: String,
        private val onDenied: () -> Unit = {},
        private val onShowRationale: (onRequest: () -> Unit) -> Unit = {}
) {
    private var onGranted: () -> Unit = {}

    private val launcher =
            activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
                when {
                    isGranted -> onGranted()
                    else -> onDenied()
                }
            }

    fun runWithPermission(onGranted: () -> Unit) {
        this.onGranted = onGranted
        when {
            activity.shouldShowRequestPermissionRationale(permission) -> onShowRationale {
                launcher.launch(permission)
            }
            else -> {
                launcher.launch(permission)
            }
        }
    }
}
```

- `activity.registerForActivityResult(ActivityResultContracts.RequestPermission())`は、パーミッションの判定処理を実施しています。
    - Boolean でパーミッションの状態を返却してくれます。
- `shouldShowRequestPermissionRationale()`ユーザーがパーミッション判定に回答したかを返却します。

### 利用ケース

``` SampleActivity.kt

class SampleActivity : AppCompatActivity(){
    ・・・省略

    // ファイルストレージのPermission判定
    private val externalStrageAccessPermissionChecker = PermissionChecker(this, WRITE_EXTERNAL_STORAGE,
            onDenied = {
                // 拒否した際の、通知処理
                Toast.makeText(this, "アクセス拒否しているから、処理できませんでした。", Toast.LENGTH_LONG).show()
            },
            onShowRationale = { onRequest ->
                // 一度拒否した後の、処理実施時の処理の説明を実施
                AlertDialog.Builder(this)
                        .setPositiveButton("次へ")
                        { _, _ -> onRequest() }
                        .setCancelable(false)
                        .setMessage("ファイルアクセス許可が必要なんです。")
                        .show()
            }
    )
    // ファイルダウンロード処理
    fun downloadFile(url: String, name: String) {
        externalStrageAccessPermissionChecker.runWithPermission(
                onGranted = {
                    // ダウンロード処理を実施
                }
        )
    }
}

```
- `PermissionChecker`は、`registerForActivityResult()`の処理を実装しています。この処理はonCreate()前に呼び出す必要があります。
- `runWithPermission()`の`onGranted()`はユーザーが許可した際に呼び出されます。
- `onShowRationale()`は一度処理を拒否した後に呼び出されます。`onRequest()`を呼び出すことで、中断した処理を実施します。
- `onDenied()`は拒否された際に通知されます。

## まとめ
- `registerForActivityResult(ActivityResultContracts.RequestPermission())`を利用する事で、いままで実装が大変だった、処理が大幅に楽になります。
- `PermissionsDispatcher`も便利ですが、Android 11 対応時には、利用箇所でワーニングが多く表示されるケースがあります。
- ワーニングをなくす為には処理の置き換えが必要かな:expressionless:


#### 追記(2020/11/11)
- `PermissionChecker`の作者 @hotchemi さんから ktx版の情報をいただいたので追記しておきます！
- ライブラリの置き換えであれば、こちらのktx版を利用するのが楽そうですね:smiley:  
https://github.com/permissions-dispatcher/PermissionsDispatcher/tree/master/ktx