Shiraji's Blog

PermissionsDispatcher V3に関して

この記事は Android Advent Calendar 2017 の5日目の記事です。

はじめに

PermissionsDispatcher v3が今年の9月にリリースされました🎉

結構大きな修正であったので、これに関して書きます。

自己紹介

PermissionsDispatcherのコミッターの一人です。今年はそれほどコードでの貢献が出来なかったので、この記事を書くことで何か貢献したいと思います。

想定読者

  • PermissionsDispatcherを使ったことがある人
  • PermissionsDispatcher v3に興味がある人

書いていないこと

  • PermissionsDispatcherの使い方
  • KotlinPoetに関して

PermissionsDispatcher v3への移行方法

まずv2を使っている人はv3への移行をする必要があります。移行手順は以下にあります。

https://github.com/permissions-dispatcher/PermissionsDispatcher/blob/master/doc/migration_guide.md#migrating-to-3x

簡単に解説すると以下の二点の修正が必要になります。

  • KotlinユーザはXxxPermissionsDispatcherクラスが無くなり、Activity/Fragmentの拡張関数として提供されるようになった
  • Java/Kotlin共にXxxWithCheckメソッドがXxxWithPermissionCheckメソッドに変更になった

生成されるKotlinコード

実際に生成されるKotlinのソースを見てみましょう。sample-kotlinモジュールでは以下のコードが生成されます。

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
76
77
78
79
80
81
82
83
84
85
86
87
88
private val REQUEST_SHOWCAMERA: Int = 0

private val PERMISSION_SHOWCAMERA: Array<String> = arrayOf("android.permission.CAMERA")

private val REQUEST_SHOWCONTACTS: Int = 1

private val PERMISSION_SHOWCONTACTS: Array<String> = arrayOf("android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS")

fun MainActivity.showCameraWithPermissionCheck() {
  if (PermissionUtils.hasSelfPermissions(this, *PERMISSION_SHOWCAMERA)) {
    showCamera()
  } else {
    if (PermissionUtils.shouldShowRequestPermissionRationale(this, *PERMISSION_SHOWCAMERA)) {
      showRationaleForCamera(MainActivityShowCameraPermissionRequest(this))
    } else {
      ActivityCompat.requestPermissions(this, PERMISSION_SHOWCAMERA, REQUEST_SHOWCAMERA)
    }
  }
}

fun MainActivity.showContactsWithPermissionCheck() {
  if (PermissionUtils.hasSelfPermissions(this, *PERMISSION_SHOWCONTACTS)) {
    showContacts()
  } else {
    if (PermissionUtils.shouldShowRequestPermissionRationale(this, *PERMISSION_SHOWCONTACTS)) {
      showRationaleForContacts(MainActivityShowContactsPermissionRequest(this))
    } else {
      ActivityCompat.requestPermissions(this, PERMISSION_SHOWCONTACTS, REQUEST_SHOWCONTACTS)
    }
  }
}

fun MainActivity.onRequestPermissionsResult(requestCode: Int, grantResults: IntArray) {
  when (requestCode) {
    REQUEST_SHOWCAMERA ->
     {
      if (PermissionUtils.verifyPermissions(*grantResults)) {
        showCamera()
      } else {
        if (!PermissionUtils.shouldShowRequestPermissionRationale(this, *PERMISSION_SHOWCAMERA)) {
          onCameraNeverAskAgain()
        } else {
          onCameraDenied()
        }
      }
    }
    REQUEST_SHOWCONTACTS ->
     {
      if (PermissionUtils.verifyPermissions(*grantResults)) {
        showContacts()
      } else {
        if (!PermissionUtils.shouldShowRequestPermissionRationale(this, *PERMISSION_SHOWCONTACTS)) {
          onContactsNeverAskAgain()
        } else {
          onContactsDenied()
        }
      }
    }
  }
}

private class MainActivityShowCameraPermissionRequest(target: MainActivity) : PermissionRequest {
  private val weakTarget: WeakReference<MainActivity> = WeakReference(target)

  override fun proceed() {
    val target = weakTarget.get() ?: return
    ActivityCompat.requestPermissions(target, PERMISSION_SHOWCAMERA, REQUEST_SHOWCAMERA)
  }

  override fun cancel() {
    val target = weakTarget.get() ?: return
    target.onCameraDenied()
  }
}

private class MainActivityShowContactsPermissionRequest(target: MainActivity) : PermissionRequest {
  private val weakTarget: WeakReference<MainActivity> = WeakReference(target)

  override fun proceed() {
    val target = weakTarget.get() ?: return
    ActivityCompat.requestPermissions(target, PERMISSION_SHOWCONTACTS, REQUEST_SHOWCONTACTS)
  }

  override fun cancel() {
    val target = weakTarget.get() ?: return
    target.onContactsDenied()
  }
}

REQUEST_*PERMISSION_*はv2ではMainActivityPermissionsDispatcherクラス内で定義されていましたが、privateのトップレベルでの定義に変わりました。 また、showCameraWithPermissionCheckonRequestPermissionsResultMainActivityの拡張関数になっています。その為、MainActivityPermissionsDispatcherクラスがなくなりました。(Javaの方では存在しています。)

なんでこんな変更になったのか?

Kotlinの拡張関数

KotlinPoetの出現から、これを使ってKotlinコードの生成してみよう->どうせならKotlinっぽく拡張関数にしてみよう!という流れで拡張関数を使うことになりました。

詳細は以下のIssueを確認してみて下さい。

https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/320

XxxWithPermissionCheckへのリネーム

以下のIssueで問題提起されました。

https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/355

このIssueでは漠然とした質問でしたが、Kotlinユーザに対して、拡張関数として提供する場合、WithCheckでは意味がわからないのではないか?という話から一気にv3.0に導入する流れになりました。

懸念事項

Kotlin向けのlint

PermissionsDispatcherはカスタムlintを提供していますが、Kotlin実装向けのlintの対応がまだ終わっていません。(実際の問題はUASTのKotlin対応)

この件に関するIssueは以下

https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/373

Intellij Plugin

permissions-dispatcher-pluginと言うIntellijプラグインですが、メイン開発者の @shiraji がサボっている為、v3対応がされていません。またもうv2の対応しなくて良くね?と言っており、誰からも反論がない為、急にv3のみのサポートになる予定です。

そんなにいないと思いますが・・・このプラグインを使っていて、v2のみしか使っていない人はこれを機にv3へのアップグレードをよろしくお願いいたします。多分今年度中にはプラグインの方も対応します。

あんまり利用者関係ない話

permissions-dispatcher orgが出来た

@hotchemiさんからの提案で、permissions-dispatcher orgが出来ました。おかげ様で、ますますメンテナとして頑張りたい!と思えるように。以下がそのIssue

https://github.com/permissions-dispatcher/PermissionsDispatcher/issues/353

数日前にあったPermissionsDispatcherのプロダクトマネジメントにも同じようなことが記述されていました。

挙動を確認するテストを増やした

7月に書いたTesting Against Annotation Processingに詳細書いてあるので、読んでください!!!

KotlinPoetに関して

PermissionsDispatcherのこの辺りを眺めるとKotlinPoetの実装の参考になると思います。Kotlin Advent Calendar の16日目にKotlinPoetに関する投稿がされるので、そちらも参照して下さい。

最後に

PermissionsDispatcherは非常に良いライブラリで、新しい仕組みも積極的に取り入れています。一緒に開発しませんか?

また、最近、ビルド出来ない。使い方がわからない。などなどREADME読んでません的なissueに上がってきます。これの対応に時間を割いてしまい、開発が滞りガチです。もし、PermissionsDispatcherに対して何か貢献したい!と言う方は、 このようなissueの対応や調査などなどお手伝いして頂けると非常に助かります。