🐻‍❄️

Jetpack Compose で Permission を要求する方法

2021/11/13に公開

はじめに

Jetpack Compose で Permission を要求するには rememberLauncherForActivityResult を利用するかまたは AccompanistPermissions を利用する方法の2通りがある。

セットアップ

本記事では以下の環境で動作確認をしています。

  • Kotlin v1.5.21
  • Jetpack Compose v1.0.1
  • Android Studio Bublebee | 2021.1.1 Canary 7

また Accompanist の Permission を利用するので以下の Dependencies を追加しています。

dependencies {
		︙
    implementation "com.google.accompanist:accompanist-permissions:0.16.1"
		︙
}

特定の画面が表示されたら Permission を要求する

特定の画面が表示されたら Permission を要求する場合は rememberLauncherForActivityResult を利用する。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            RequestPermission()
        }
    }
}

@Composable
private fun RequestPermission() {
    // LocalComposition より提供される Context を取得する
    val context = LocalContext.current

    // LocalComposition より提供される Lifecycle を取得する
    val lifecycle = LocalLifecycleOwner.current.lifecycle

    // Permission Request を実行する Permission を定義する
    val permission = Manifest.permission.READ_EXTERNAL_STORAGE

    // Permission Request を実行するための Launcher を作成する
    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestPermission(),
        onResult = { isGranted -> Log.v("TEST", "PERMISSION REQUEST RESULT ${isGranted}") }
    )

    // もし Permission が許可されていなければ、 Activity が onStart に遷移したとき、
    // Launcher を利用して Permission Request を実行する LifecycleObserver を作成する。
    val lifecycleObserver = remember {
        LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_START) {
                if (!permission.isGrantedPermission(context)) {
                    launcher.launch(permission)
                }
            }
        }
    }

    // lifecycle または lifecycleObserver が変化した、また破棄されたら呼び出される
    DisposableEffect(lifecycle, lifecycleObserver) {
        lifecycle.addObserver(lifecycleObserver)
        onDispose {
            lifecycle.removeObserver(lifecycleObserver)
        }
    }
}

private fun String.isGrantedPermission(context: Context): Boolean {
    // checkSelfPermission は PERMISSION_GRANTED or PERMISSION_DENIED のどちらかを返す
    // そのため checkSelfPermission の戻り値が PERMISSION_GRANTED であれば許可済みになる。
    return context.checkSelfPermission(this) == PackageManager.PERMISSION_GRANTED
}

起動する以下のように MainActivity が表示されたら Permission 要求が表示される

demo1-1.gif

特定のボタンを押したら Permission を要求する

特定のボタンを押したら Permission を要求する場合でも rememberLauncherForActivityResult を利用して実装もできる。

だが Accompanist の Permissions を利用したほうが以下のように楽に実装ができる。(ですが v0.16.1 だと Experimental です)

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            RequestPermissionUsingAccompanist()
        }
    }
}

@OptIn(ExperimentalPermissionsApi::class)
@Composable
private fun RequestPermissionUsingAccompanist() {
    // Permission Request を実行する Permission を定義する
    val permission = Manifest.permission.READ_EXTERNAL_STORAGE

    // Permission Request の実行を制御する State クラス
    val permissionState = rememberPermissionState(permission)
    PermissionRequired(
        permissionState = permissionState,
        permissionNotAvailableContent = {
            // Permission を拒否し、表示しないを押したときの View
            Text("Permission Denied.")
        }, permissionNotGrantedContent = {
            // Permission の許可を促すときの View
            Button(onClick = { permissionState.launchPermissionRequest() }) {
                Text("Request permission.")
            }
        }, content = {
            // Permission が許可されたときの View
            Text("Permission Granted.")
        }
    )
}

起動すると permissionNotGrantedContent の Button が表示される、Button を押すと Permission 要求が表示される。

Permission 要求にて Allow を選択すると Permission が許可されたので content の View が表示される。

demo-2.gif

もし Permissiyon 要求にて Deny & dont’t ask again を押したときには permissionNotAvailableContent の View が表示される。

demo-3.gif

参考文献

Discussion