Skip to content

Instantly share code, notes, and snippets.

@bnutz
Last active October 11, 2020 10:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bnutz/b36765f8d47732b7b286a31047086760 to your computer and use it in GitHub Desktop.
Save bnutz/b36765f8d47732b7b286a31047086760 to your computer and use it in GitHub Desktop.
Base flow for handling Android permission request and response in Kotlin

To be placed in a main parent Activity, or as part of some custom BaseActivity class for handling permission request and responses without losing the "flow".

Example usage:

btn_load_timeline?.setOnClickListener {
    permissionCheck(Manifest.permission.READ_EXTERNAL_STORAGE, PermissionRequestCode.TIMELINE_FEATURE_EXTERNAL_STORAGE) { permissionResponse ->
        when (permissionResponse) {
            PermissionResponse.PERMISSION_GRANTED -> {
                Log.d(TAG, "Permission granted, load timeline here")
                // Do stuff with granted permission
            }
            PermissionResponse.PERMISSION_DENIED -> {
                Log.d(TAG, "Permission Denied, but can still prompt again")
                // Can prompt for permission again, or show rationale, etc.
            }
            PermissionResponse.PERMISSION_DONT_SHOW_AGAIN -> {
                Log.d(TAG, "Permission Denied, and user selected 'Don't Show Again'")
                // Disable feature, and/or let user know they'll need to go to Settings > App Info to enable the permission
            }
        }
    }
}
package com.justbnutz.labs
import android.content.pm.PackageManager
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.snackbar.Snackbar
import com.justbnutz.labs.models.PermissionRequestCode
import com.justbnutz.labs.models.PermissionResponse
import java.util.*
/**
* Common methods between all activities go here
*/
abstract class BaseActivity : AppCompatActivity() {
// A queue of actions to take after we get the permission response callback, indexed by the request code enum
private val permissionResponseQueue: EnumMap<PermissionRequestCode, (PermissionResponse) -> Unit> by lazy { EnumMap(PermissionRequestCode::class.java) }
fun permissionCheck(requestedPermission: String, requestCode: PermissionRequestCode, permissionResponse: (PermissionResponse) -> Unit) {
if (checkSelfPermission(requestedPermission) == PackageManager.PERMISSION_GRANTED) {
// We have permission, can immediately execute the response action
permissionResponse(PermissionResponse.PERMISSION_GRANTED)
} else {
// Permission not (yet) granted, trigger a request and handle the rest of the flow in onRequestPermissionsResult()
requestPermissions(arrayOf(requestedPermission), requestCode.ordinal)
// Queue up the response action so we can pick it up again in onRequestPermissionsResult() callback
permissionResponseQueue[requestCode] = permissionResponse
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
// Check if requestCode matches an ordinal key value in permissionResponseQueue
permissionResponseQueue.entries.firstOrNull { it.key.ordinal == requestCode }?.also { (key, permissionResponse) ->
// Note: Can only handle singular permission requests for now: TODO: Handle multi-permission requests
permissions.firstOrNull()?.let { requestedPermission ->
grantResults.firstOrNull()?.let { permissionResult ->
when {
(permissionResult == PackageManager.PERMISSION_GRANTED) -> {
// Permission granted, do the action
permissionResponse(PermissionResponse.PERMISSION_GRANTED)
}
shouldShowRequestPermissionRationale(requestedPermission) -> {
// Permission previously denied, but "Don't Show Again" left unchecked; can prompt again
permissionResponse(PermissionResponse.PERMISSION_DENIED)
}
else -> {
// Permission previously denied and "Don't show again" was checked; need to provide link to App Info / Settings.
permissionResponse(PermissionResponse.PERMISSION_DONT_SHOW_AGAIN)
}
}
}
}
// Response handled; remove the action item from the queue
permissionResponseQueue.remove(key)
} ?: run {
// Request code is not part of the queue, let system handle response
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
fun showSnackbar(view: View, message: String) {
Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show()
}
fun showToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
package com.justbnutz.labs.models
/**
* Can use any name here - one entry for each permission journey.
* Allows the onRequestPermissionsResult callback to know which
* which journey to return to upon receiving the response.
*
* Use PermissionRequestCodes.<permission_request_flow>.ordinal
* to get the Int value and use these as callback request codes
*/
enum class PermissionRequestCode {
TIMELINE_FEATURE_EXTERNAL_STORAGE,
SHARE_FEATURE_READ_CONTACT,
ANY_DESCRIPTIVE_NAME_BLAH_BLAH
}
package com.justbnutz.labs.models
/**
* Denotes the three possible paths that could come from a permission request.
*/
enum class PermissionResponse {
PERMISSION_GRANTED,
PERMISSION_DENIED,
PERMISSION_DONT_SHOW_AGAIN
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment