Kotlin でアプリに実行時の権限を追加する

1. ようこそ

はじめに

アプリの権限は、アプリのサンドボックスによって提供される以外のデータと機能へのアクセスを提供するために使用されます。インターネット、デバイスの位置情報、カメラなどへのアクセスを許可することで、アプリの機能を強化できます。

こうした機能を使用するには、ユーザーに権限をリクエストする必要があります。この Codelab では、実行時の権限の追加と権限の有無の確認に必要な手順について説明します。

必要なもの

前提となる知識

学習内容

  • Android マニフェストに権限を追加する方法
  • 権限をリクエストする方法
  • 権限が許可された場合と拒否された場合の対応方法
  • 権限が付与されたかどうかを確認する方法

作成するアプリの概要

カメラへのアクセス権限をリクエストするアプリを作成します。アプリの権限に関する処理は実装しますが、カメラに関する処理は実装しません。

2. Android Studio プロジェクトを作成する

  1. 新しい Android Studio プロジェクトを開始します。
  2. [Empty Activity] を選択します。

70e5de28256e3dcb.png

  1. プロジェクトの名前を「Permissions Codelab」とし、言語を「Kotlin」に設定します。

f2948b584ca99c47.png

3. コードをセットアップする

  1. 以下の最新バージョンの依存関係をアプリレベルの build.gradle ファイルに追加します。追加すると、この Codelab で後述する Activity ライブラリを使用できます。
implementation "androidx.activity:activity-ktx:1.2.2"
implementation "androidx.fragment:fragment-ktx:1.3.2"
  1. android ブロックで viewBinding ビルド オプションを true に設定して、ViewBinding を有効にします。
android {
   ...
   buildFeatures {
       viewBinding true
   }
}
  1. 緑色のハンマーボタンをクリックしてビルドします。これにより、後で ViewBinding に使用する ActivityMainBinding というバインディング クラスが生成されます。

d4064454e5c50111.png

  1. activity_main.xml ファイルに移動し、コードを次のコードに置き換えます。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/main_layout"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <Button
       android:id="@+id/button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Request Permissions"
       android:onClick="onClickRequestPermission"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  1. 次の文字列を strings.xml ファイルに追加します。
<string name="app_name">Permissions Codelab</string>
<string name="permission_required">Camera access is required to display the camera preview.</string>
<string name="ok">OK</string>
<string name="permission_granted">Permission is granted. You can use the camera now.</string>
  1. MainActivity.ktonCreate() メソッドの上に、レイアウトと ViewBinding の変数を定義します。
private lateinit var layout: View
private lateinit var binding: ActivityMainBinding
  1. onCreate() メソッドのコードを次のコードで置き換えます。このコードは、バインディングを初期化し、ビューを表す val を作成してバインディングのルートに設定し、レイアウトをバインディングの mainLayout に設定します。
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   binding = ActivityMainBinding.inflate(layoutInflater)
   val view = binding.root
   layout = binding.mainLayout
   setContentView(view)
}
  1. ファイルの下部にある MainActivity クラスの下に、Codelab 全体を通してスナックバーを表示する拡張関数を追加します。
fun View.showSnackbar(
   view: View,
   msg: String,
   length: Int,
   actionMessage: CharSequence?,
   action: (View) -> Unit
) {
   val snackbar = Snackbar.make(view, msg, length)
   if (actionMessage != null) {
       snackbar.setAction(actionMessage) {
           action(this)
       }.show()
   } else {
       snackbar.show()
   }
}

4. マニフェストに権限を追加する

まず必要なのは、Android manifest<uses-permission> タグを使って権限の使用を宣言することです。

リクエストする権限によっては、その他の要件がある場合もあります。この場合、デバイスにカメラが搭載されていない限り、カメラアプリを使用できません。このため、マニフェストに <uses-feature> タグも追加します。

  1. AndroidManifest.xml ファイルの <application> タグの上にカメラの権限を追加します。
<uses-permission android:name="android.permission.CAMERA" />

5. 権限ランチャーを作成する

MainActivity.kt で、requestPermissionLauncher という val を作成し、次のコードをコピーします。このコードの内容を以下の箇条書きに分けて説明します。

private val requestPermissionLauncher =
   registerForActivityResult(
       ActivityResultContracts.RequestPermission()
   ) { isGranted: Boolean ->
       if (isGranted) {
           Log.i("Permission: ", "Granted")
       } else {
           Log.i("Permission: ", "Denied")
       }
   }
private val requestPermissionLauncher =
   registerForActivityResult(
       ActivityResultContracts.RequestPermission())
  • 権限が許可されたケース、許可されなかったケースを処理するコールバックを追加します。この例では、結果をログに記録しています。
{ isGranted: Boolean ->
       if (isGranted) {
           Log.i("Permission: ", "Granted")
       } else {
           Log.i("Permission: ", "Denied")
       }
}

6. 権限をリクエストする

  1. onClickRequestPermission(view: View) という関数を作成し、このコードをコピーします。このコードの内容を以下の箇条書きに分けて説明します。
fun onClickRequestPermission(view: View) {
   when {
       ContextCompat.checkSelfPermission(
           this,
           Manifest.permission.CAMERA
       ) == PackageManager.PERMISSION_GRANTED -> {
           layout.showSnackbar(
               view,
               getString(R.string.permission_granted),
               Snackbar.LENGTH_INDEFINITE,
               null
           ) {}
       }

       ActivityCompat.shouldShowRequestPermissionRationale(
           this,
           Manifest.permission.CAMERA
       ) -> {
           layout.showSnackbar(
               view,
               getString(R.string.permission_required),
               Snackbar.LENGTH_INDEFINITE,
               getString(R.string.ok)
           ) {
               requestPermissionLauncher.launch(
                   Manifest.permission.CAMERA
               )
           }
       }

       else -> {
           requestPermissionLauncher.launch(
               Manifest.permission.CAMERA
           )
       }
   }
}
  • すでに権限が付与されている場合、アプリがリクエスト権限の根拠を示すべきと判断した場合、まだ権限がリクエストされていない場合、以上 3 つのケースを扱う when ステートメントを設定します。
when {
   ContextCompat.checkSelfPermission(
       this,
       Manifest.permission.CAMERA
   ) == PackageManager.PERMISSION_GRANTED -> {

   }

   ActivityCompat.shouldShowRequestPermissionRationale(
       this,
       Manifest.permission.CAMERA
   ) -> {

   }

   else -> {

   }
}
  • 権限が付与されている場合は、その旨を知らせるスナックバーを表示します。
ContextCompat.checkSelfPermission(
   this,
   Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED -> {
   layout.showSnackbar(
       view,
       getString(R.string.permission_granted),
       Snackbar.LENGTH_INDEFINITE,
       null
   ) {}
}
  • shouldShowRequestPermissionRationale() が true を返す場合は、その機能が特定の権限を必要とする理由を詳しく説明する UI を表示し、ユーザーが UI から権限を承認または拒否できるようにします。

a25d8c6c1d5051d.png

ActivityCompat.shouldShowRequestPermissionRationale(
   this,
   Manifest.permission.CAMERA
) -> {
   layout.showSnackbar(
       view,
       getString(R.string.permission_required),
       Snackbar.LENGTH_INDEFINITE,
       getString(R.string.ok)
   ) {
       requestPermissionLauncher.launch(
           Manifest.permission.CAMERA
       )
   }
}
  • 上記以外の場合は、権限をリクエストします。
else -> {
   requestPermissionLauncher.launch(
       Manifest.permission.CAMERA
   )
}

7. まとめ

これで完了です。この Codelab では、実行時の権限を追加する方法について説明しています。この方法は、位置情報、ネットワーク、メディアなどに関する権限に使用できます。

最終的なコードについては、ドキュメントをご覧ください。