Skip to content

Instantly share code, notes, and snippets.

@whileloop99
Last active November 2, 2024 04:52
Show Gist options
  • Select an option

  • Save whileloop99/d8bc8b2e62d53226b88703798ea98c74 to your computer and use it in GitHub Desktop.

Select an option

Save whileloop99/d8bc8b2e62d53226b88703798ea98c74 to your computer and use it in GitHub Desktop.
Kotlin: Better Way to Request Runtime Permissions
package your.business
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
/**
* Request permissions
*
* Usage:
* val allGranted = requestAppPermissions(context, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) { notGrantedPermissions ->
* // Some error messages?
* }
*
* if (allGranted) {
* startCamera()
* }
*/
suspend fun requestPermissions(
activity: FragmentActivity,
vararg permissions: String,
nonShowRequestPermissionRationaleHandler: ((List<String>) -> Unit)? = null
): Boolean = suspendCoroutine { emitter ->
val nonShowRequestPermissions = mutableListOf<String>()
val nonGrantedPermissions = mutableListOf<String>()
var allGranted = true
permissions.forEach { perm ->
val granted = ActivityCompat.checkSelfPermission(activity, perm) == PackageManager.PERMISSION_GRANTED
if (!granted) {
allGranted = false
if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, perm)) {
nonShowRequestPermissions.add(perm)
} else {
nonGrantedPermissions.add(perm)
}
}
}
if (allGranted) {
emitter.resume(true)
return@suspendCoroutine
}
class RequestPermissionFragment : Fragment() {
private lateinit var activityResultLauncher: ActivityResultLauncher<Array<String>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true // Retain fragment across configuration changes
activityResultLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { resultMap ->
val grantedPermissions = resultMap.values.all { it }
allGranted = grantedPermissions && nonShowRequestPermissions.isEmpty()
if (nonShowRequestPermissions.isNotEmpty()) {
nonShowRequestPermissionRationaleHandler?.invoke(nonShowRequestPermissions)
}
emitter.resume(allGranted)
// Safely remove the fragment after permission result
if (!activity.supportFragmentManager.isDestroyed) {
activity.supportFragmentManager.beginTransaction()
.remove(this@RequestPermissionFragment)
.commitAllowingStateLoss()
}
}
activityResultLauncher.launch(nonGrantedPermissions.toTypedArray())
}
}
// Attach fragment with a tag to prevent duplicates
if (activity.supportFragmentManager.findFragmentByTag("RequestPermissionFragment") == null) {
activity.supportFragmentManager.beginTransaction()
.add(RequestPermissionFragment(), "RequestPermissionFragment")
.commitAllowingStateLoss()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment