mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-19 07:15:25 +02:00
feat: Improve permission request
This commit is contained in:
parent
3f124a24e9
commit
1c49ac35b8
@ -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)
|
||||||
},
|
},
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
@ -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(it),
|
arrayOf(permission),
|
||||||
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() {
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user