feat: Improve permission request

This commit is contained in:
Myzel394 2023-08-08 10:11:29 +02:00
parent 3f124a24e9
commit 1c49ac35b8
No known key found for this signature in database
GPG Key ID: 79CC92F37B3E1A2B
5 changed files with 121 additions and 46 deletions

View File

@ -69,13 +69,8 @@ fun StartRecording(
) { ) {
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
PermissionRequester( PermissionRequester(
permission = arrayOf(Manifest.permission.RECORD_AUDIO), permission = Manifest.permission.RECORD_AUDIO,
icon = { icon = Icons.Default.Mic,
Icon(
Icons.Default.Mic,
contentDescription = null,
)
},
onPermissionAvailable = { onPermissionAvailable = {
RecorderService.startService(context, connection) RecorderService.startService(context, connection)
}, },

View File

@ -37,7 +37,7 @@ fun MessageBox(
} }
val onContainerColor = when(type) { val onContainerColor = when(type) {
MessageType.ERROR -> MaterialTheme.colorScheme.onError MessageType.ERROR -> MaterialTheme.colorScheme.onError
MessageType.INFO -> MaterialTheme.colorScheme.onTertiary MessageType.INFO -> MaterialTheme.colorScheme.onTertiaryContainer
MessageType.SUCCESS -> Color.Green MessageType.SUCCESS -> Color.Green
MessageType.WARNING -> Color.Yellow MessageType.WARNING -> Color.Yellow
} }

View File

@ -1,10 +1,17 @@
package app.myzel394.alibi.ui.components.atoms package app.myzel394.alibi.ui.components.atoms
import android.app.Activity
import android.content.pm.PackageManager
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.OpenInNew import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
@ -12,55 +19,136 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.app.ActivityCompat
import app.myzel394.alibi.R import app.myzel394.alibi.R
import app.myzel394.alibi.ui.utils.PermissionHelper import app.myzel394.alibi.ui.utils.PermissionHelper
import app.myzel394.alibi.ui.utils.openAppSystemSettings import app.myzel394.alibi.ui.utils.openAppSystemSettings
@Composable @Composable
fun PermissionRequester( fun PermissionRequester(
permission: Array<String>, permission: String,
icon: (@Composable () -> Unit)? = null, icon: ImageVector,
onPermissionAvailable: () -> Unit, onPermissionAvailable: () -> Unit,
content: @Composable (trigger: () -> Unit) -> Unit content: @Composable (trigger: () -> Unit) -> Unit
) { ) {
val context = LocalContext.current val context = LocalContext.current
var showExplanationDialog by remember { mutableStateOf(false) } var isGranted by remember { mutableStateOf(PermissionHelper.hasGranted(context, permission)) }
var visibleDialog by remember { mutableStateOf<VisibleDialog?>(null) }
val requestPermission = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { isPermissionGranted: Boolean ->
isGranted = isPermissionGranted
if (isGranted) {
onPermissionAvailable()
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(context as Activity, permission)) {
visibleDialog = VisibleDialog.REQUEST
} else {
visibleDialog = VisibleDialog.PERMANENTLY_DENIED
}
}
}
)
fun callback() { fun callback() {
if (PermissionHelper.hasPermanentlyDenied(context, permission)) { if (isGranted) {
showExplanationDialog = true
return
}
if (PermissionHelper.checkPermissions(context, permission)) {
onPermissionAvailable() onPermissionAvailable()
} else {
requestPermission.launch(permission)
} }
} }
if (showExplanationDialog) { if (visibleDialog == VisibleDialog.REQUEST) {
AlertDialog( AlertDialog(
onDismissRequest = { onDismissRequest = {
showExplanationDialog = false visibleDialog = null
},
icon = {
Icon(
icon,
contentDescription = null,
)
}, },
icon = icon,
title = { title = {
Text(stringResource(R.string.ui_permissions_request_title)) Text(stringResource(R.string.ui_permissions_request_title))
}, },
text = { text = {
Text(stringResource(R.string.ui_permissions_request_message)) Text(stringResource(R.string.ui_permissions_request))
}, },
confirmButton = { confirmButton = {
Button( Button(
onClick = { onClick = {
showExplanationDialog = false visibleDialog = null
callback()
}
) {
Icon(
Icons.Default.Check,
contentDescription = null,
modifier = Modifier.size(ButtonDefaults.IconSize),
)
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text(stringResource(R.string.dialog_close_neutral_label))
}
},
dismissButton = {
Button(
onClick = {
visibleDialog = null
},
colors = ButtonDefaults.textButtonColors(),
) {
Icon(
Icons.Default.Cancel,
contentDescription = null,
modifier = Modifier.size(ButtonDefaults.IconSize),
)
Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
Text(stringResource(R.string.dialog_close_cancel_label))
}
}
)
}
if (visibleDialog == VisibleDialog.PERMANENTLY_DENIED) {
AlertDialog(
onDismissRequest = {
visibleDialog = null
},
icon = {
Icon(
icon,
contentDescription = null,
)
},
title = {
Text(stringResource(R.string.ui_permissions_request_title))
},
text = {
Column {
Text(stringResource(R.string.ui_permissions_request))
Spacer(modifier = Modifier.height(32.dp))
MessageBox(
type = MessageType.INFO,
message = stringResource(R.string.ui_permissions_permanentlyDenied_message)
)
}
},
confirmButton = {
Button(
onClick = {
visibleDialog = null
context.openAppSystemSettings() context.openAppSystemSettings()
} }
) { ) {
@ -76,7 +164,7 @@ fun PermissionRequester(
dismissButton = { dismissButton = {
Button( Button(
onClick = { onClick = {
showExplanationDialog = false visibleDialog = null
}, },
colors = ButtonDefaults.textButtonColors(), colors = ButtonDefaults.textButtonColors(),
) { ) {
@ -93,3 +181,8 @@ fun PermissionRequester(
} }
content(::callback) content(::callback)
} }
enum class VisibleDialog {
REQUEST,
PERMANENTLY_DENIED
}

View File

@ -10,34 +10,20 @@ import androidx.core.app.ActivityCompat
// From @Bnyro // From @Bnyro
object PermissionHelper { object PermissionHelper {
fun checkPermissions(context: Context, permissions: Array<String>): Boolean { fun checkPermission(context: Context, permission: String): Boolean {
permissions.forEach { if (!hasGranted(context, permission)) {
if (!hasGranted(context, it)) { ActivityCompat.requestPermissions(
ActivityCompat.requestPermissions( context as Activity,
context as Activity, arrayOf(permission),
arrayOf(it), 1
1 )
) return false
return false
}
} }
return true return true
} }
fun hasGranted(context: Context, permission: String): Boolean = fun hasGranted(context: Context, permission: String): Boolean =
ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
fun hasPermanentlyDenied(context: Context, permission: String): Boolean =
!hasGranted(context, permission) &&
!ActivityCompat.shouldShowRequestPermissionRationale(context as Activity, permission)
fun hasPermanentlyDenied(context: Context, permission: Array<String>): Boolean {
permission.forEach {
if (hasPermanentlyDenied(context, it))
return true
}
return false
}
} }
fun Context.openAppSystemSettings() { fun Context.openAppSystemSettings() {

View File

@ -15,7 +15,8 @@
<string name="notificationChannels_recorder_description">Shows the current recording status</string> <string name="notificationChannels_recorder_description">Shows the current recording status</string>
<string name="ui_permissions_request_title">Permission denied</string> <string name="ui_permissions_request_title">Permission denied</string>
<string name="ui_permissions_request_message">Please grant the permission to continue. You will be redirected to the app settings to grant the permission there.</string> <string name="ui_permissions_request">Please grant the permission to continue</string>
<string name="ui_permissions_permanentlyDenied_message">You will be redirected to the app settings to grant the permission there.</string>
<string name="ui_audioRecorder_action_start_label">Start Recording</string> <string name="ui_audioRecorder_action_start_label">Start Recording</string>
<string name="ui_audioRecorder_action_saveOldRecording_label">Save Recording from <xliff:g name="date">%s</xliff:g></string> <string name="ui_audioRecorder_action_saveOldRecording_label">Save Recording from <xliff:g name="date">%s</xliff:g></string>