refactor(app): improve permissions management

This commit is contained in:
ThibaultBee
2022-11-12 15:38:30 +01:00
parent 968134d9e8
commit 544b648c91
4 changed files with 93 additions and 23 deletions

View File

@@ -16,6 +16,7 @@ import io.github.thibaultbee.streampack.data.VideoConfig
import io.github.thibaultbee.streampack.error.StreamPackError import io.github.thibaultbee.streampack.error.StreamPackError
import io.github.thibaultbee.streampack.example.databinding.ActivityMainBinding import io.github.thibaultbee.streampack.example.databinding.ActivityMainBinding
import io.github.thibaultbee.streampack.example.utils.PermissionsManager import io.github.thibaultbee.streampack.example.utils.PermissionsManager
import io.github.thibaultbee.streampack.example.utils.showDialog
import io.github.thibaultbee.streampack.example.utils.toast import io.github.thibaultbee.streampack.example.utils.toast
import io.github.thibaultbee.streampack.ext.rtmp.streamers.CameraRtmpLiveStreamer import io.github.thibaultbee.streampack.ext.rtmp.streamers.CameraRtmpLiveStreamer
import io.github.thibaultbee.streampack.ext.srt.streamers.CameraSrtLiveStreamer import io.github.thibaultbee.streampack.ext.srt.streamers.CameraSrtLiveStreamer
@@ -36,13 +37,30 @@ class MainActivity : AppCompatActivity() {
private val permissionsManager = PermissionsManager(this, private val permissionsManager = PermissionsManager(this,
streamerRequiredPermissions, streamerRequiredPermissions,
onAllGranted = { inflateStreamer() }, onAllGranted = { inflateStreamer() },
onShowPermissionRationale = { true }, onShowPermissionRationale = { permissions, onRequiredPermissionLastTime ->
onDenied = {}) // Explain why we need permissions
showDialog(
title = "Permissions denied",
message = "Explain why you need to grant $permissions permissions to stream",
positiveButtonText = R.string.accept,
onPositiveButtonClick = { onRequiredPermissionLastTime() },
negativeButtonText = R.string.denied
)
},
onDenied = {
showDialog(
"Permissions denied",
"You need to grant all permissions to stream",
positiveButtonText = 0,
negativeButtonText = 0
)
}
)
// Reports and manages error with [OnErrorListener] // Reports and manages error with [OnErrorListener]
private val errorListener = object : OnErrorListener { private val errorListener = object : OnErrorListener {
override fun onError(error: StreamPackError) { override fun onError(error: StreamPackError) {
toast("An error occured: $error") toast("An error occurred: $error")
} }
} }
@@ -114,8 +132,8 @@ class MainActivity : AppCompatActivity() {
} }
} }
override fun onResume() { override fun onStart() {
super.onResume() super.onStart()
permissionsManager.requestPermissions() permissionsManager.requestPermissions()
} }

View File

@@ -1,7 +1,40 @@
package io.github.thibaultbee.streampack.example.utils package io.github.thibaultbee.streampack.example.utils
import android.content.Context import android.content.Context
import android.content.DialogInterface
import android.widget.Toast import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
fun Context.toast(message: String) = fun Context.toast(message: String) =
Toast.makeText(this, message, Toast.LENGTH_SHORT).show() Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
fun Context.showDialog(
title: String,
message: String = "",
@StringRes
positiveButtonText: Int = android.R.string.ok,
@StringRes
negativeButtonText: Int = android.R.string.cancel,
onPositiveButtonClick: () -> Unit = {},
onNegativeButtonClick: () -> Unit = {}
) {
AlertDialog.Builder(this)
.setTitle(title)
.setMessage(message)
.apply {
if (positiveButtonText != 0) {
setPositiveButton(positiveButtonText) { dialogInterface: DialogInterface, _: Int ->
dialogInterface.dismiss()
onPositiveButtonClick()
}
}
if (negativeButtonText != 0) {
setNegativeButton(negativeButtonText) { dialogInterface: DialogInterface, _: Int ->
dialogInterface.dismiss()
onNegativeButtonClick()
}
}
}
.show()
}

View File

@@ -8,43 +8,60 @@ class PermissionsManager(
private val activity: ComponentActivity, private val activity: ComponentActivity,
private val requiredPermissions: List<String>, private val requiredPermissions: List<String>,
private val onAllGranted: () -> Unit, private val onAllGranted: () -> Unit,
private val onShowPermissionRationale: (List<String>) -> Boolean, private val onShowPermissionRationale: (List<String>, () -> Unit) -> Unit,
private val onDenied: (List<String>) -> Unit private val onDenied: (List<String>) -> Unit
) { ) {
private fun hasPermissions(): Boolean { private fun hasAllPermissions(): Boolean {
return requiredPermissions.all { return requiredPermissions.all {
activity.checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED activity.checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED
} }
} }
private fun hasShownAllRationales(): Boolean {
return requiredPermissions.all {
!activity.shouldShowRequestPermissionRationale(it)
}
}
fun requestPermissions() { fun requestPermissions() {
if (hasPermissions()) { if (hasAllPermissions()) {
onAllGranted() onAllGranted()
} else { } else {
var hasShownRationale = false // Either we ask for rationale or we ask for all permissions
requiredPermissions.forEach { if (requestRationales()) {
if (activity.shouldShowRequestPermissionRationale(it)) {
hasShownRationale = true
// Last chance to show rationale
if (onShowPermissionRationale(requiredPermissions)) {
requestMultiplePermissions.launch(arrayOf(it))
}
}
}
if (!hasShownRationale) {
requestMultiplePermissions.launch(requiredPermissions.toTypedArray()) requestMultiplePermissions.launch(requiredPermissions.toTypedArray())
} }
} }
} }
private fun requestRationales(): Boolean {
// List of permissions that have been denied once. In this case, we should explain why we need them.
val rationalesPermission = mutableListOf<String>()
requiredPermissions.filter {
activity.shouldShowRequestPermissionRationale(it)
}.forEach {
rationalesPermission.add(it)
}
if (rationalesPermission.isNotEmpty()) {
onShowPermissionRationale(rationalesPermission) {
requestMultiplePermissions.launch(rationalesPermission.toTypedArray())
}
}
return rationalesPermission.isEmpty()
}
private val requestMultiplePermissions = private val requestMultiplePermissions =
activity.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> activity.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
permissions.filter { !it.value }.keys.toList().let { val deniedPermissions = permissions.filter { !it.value }.keys.toList()
if (it.isNotEmpty()) { if (deniedPermissions.isNotEmpty()) {
onDenied(it) if (!hasShownAllRationales()) {
requestRationales()
} else { } else {
onAllGranted() onDenied(deniedPermissions)
} }
} else {
onAllGranted()
} }
} }
} }

View File

@@ -1,3 +1,5 @@
<resources> <resources>
<string name="app_name">My Streaming App</string> <string name="app_name">My Streaming App</string>
<string name="denied">Denied</string>
<string name="accept">Accept</string>
</resources> </resources>