Permissions

To use some Android features like access to location, the app must have permission from user.

Declare necessary permissions in the AndroidManifest.xml.

<manifest ... >
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
</manifest>

Since API 23+ you must request the dangerous permissions at runtime. If you don't declare any dangerous permissions, or if your app is installed on a device that runs Android 5.1 (API level 22) or lower, the permissions are automatically granted.

To request the dangerous permissions at runtime follow these steps.

1. Check whether the user has already granted the runtime permission that your app requires. If no, show education ui or request the runtime permission.

Check permission
if(ContextCompat.checkSelfPermission(context,
                Manifest.permission.ACCESS_FINE_LOCATION
  ) == PackageManager.PERMISSION_GRANTED) {
    // all ok
    doStuff()
}
// within context, for example activity if(checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){ doStuff() }
if(checkPermission(Manifest.permission.ACCESS_FINE_LOCATION, Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED){ doStuff() }

2. Optionally check whether your app should show a rationale. Explain to the user why your app requires this permission. In this UI, include a "cancel" or "no thanks" button that allows the user to continue using your app without granting the permission.

Code example
// within activity
if(shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)){
    showEducationUi()
}

3. Request the runtime permission in activity. System displays a runtime permission prompt.

Request permission
val REQUEST_LOCATION_PERMISSION_CODE = 1
...
requestPermissions(
    arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
    REQUEST_LOCATION_PERMISSION_CODE
)

4. Override onRequestPermissionsResult() method of activity to handle user choice.

Handle request permission result
All together

If the user denies a permission request, your app should help users understand the implications of denying the permission.

Best practices:

  • Highlight a specific part of your app's UI where there's limited functionality because your app doesn't have the necessary permission.
  • Be specific. Don't display a generic message; instead, mention which features are unavailable because your app doesn't have the necessary permission.
  • Don't block the user interface. In other words, don't display a full-screen warning message that prevents users from continuing to use your app at all.

system managing

With Activity Result Api you can allow system to manage the request code that's associated with a permissions request.

Add necessary dependencies

dependencies {
    val activity_version = "1.2.3"

    // Java language implementation
    implementation("androidx.activity:activity:$activity_version")
    // Kotlin
    implementation("androidx.activity:activity-ktx:$activity_version")
    
    val fragment_version = "1.3.4"

    // Java language implementation
    implementation("androidx.fragment:fragment:$fragment_version")
    // Kotlin
    implementation("androidx.fragment:fragment-ktx:$fragment_version")
}

Create a permission launcher with the permissions callback

Permission launcher
val requestPermissionLauncher 
    = registerForActivityResult(RequestPermission()) { isGranted: Boolean ->
        if (isGranted) {
            // permission is granted
            doStuff()
        } else {
            // Explain to the user that the feature is unavailable because the
            // features requires a permission that the user has denied.
            showEducationUI()
        }
}

Request permission via permission launcher.

Using the permission launcher
when {
    ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED -> {
        doStuff()
    }

    shouldShowRequestPermissionRationale(
              Manifest.permission.ACCESS_FINE_LOCATION) -> {
        showEducationUI()
    }

    else -> {
        // The registered ActivityResultCallback gets the result of this request.
        requestPermissionLauncher.launch(
                Manifest.permission.ACCESS_FINE_LOCATION)
    }
}

multiple permissions

You can check multiple permissions in cycle.

Check multiple permissions
// method in activity
fun hasPermissions( vararg permissions: String): Boolean = permissions.all {
    ActivityCompat.checkSelfPermission(this, it) ==
            PackageManager.PERMISSION_GRANTED
}

Use RequestMultiplePermissions class to register permission launcher.

RequestMultiplePermissions example
val requestPermissionLauncher
            = registerForActivityResult(RequestMultiplePermissions()) { map->
    map.entries.forEach {
        val permission = it.key
        val isGranted = it.value
        //...
    }
    // ...
}
Usage example
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_activity)

    val permissions = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE)

    if(hasPermissions(*permissions)) {
        // all ok
        doStuff()
    } else {
        requestPermissionLauncher.launch(permissions)
    }
    
    // ...
}