mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-16 11:16:12 -04:00
refactor: Replace MaterialAlertDialogBuilder with Compose AlertDialog
Replace all 11 instances of MaterialAlertDialogBuilder across 5 files with Compose AlertDialog. Use sealed dialog state per screen to prevent dialog stacking. Add shared ConfirmationDialog composable. Delete dead ErrorDialog.kt.
This commit is contained in:
@@ -0,0 +1,32 @@
|
|||||||
|
package eu.darken.capod.common.compose
|
||||||
|
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ConfirmationDialog(
|
||||||
|
title: String,
|
||||||
|
message: String,
|
||||||
|
confirmLabel: String,
|
||||||
|
dismissLabel: String,
|
||||||
|
onConfirm: () -> Unit,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
title = { Text(text = title) },
|
||||||
|
text = { Text(text = message) },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = onConfirm) {
|
||||||
|
Text(text = confirmLabel)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text(text = dismissLabel)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -17,10 +17,11 @@ import androidx.lifecycle.compose.LifecycleResumeEffect
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.compose.ui.graphics.luminance
|
import androidx.compose.ui.graphics.luminance
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
|
import eu.darken.capod.common.compose.ConfirmationDialog
|
||||||
import eu.darken.capod.common.debug.logging.logTag
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
import eu.darken.capod.common.theming.CapodTheme
|
import eu.darken.capod.common.theming.CapodTheme
|
||||||
import eu.darken.capod.common.uix.Activity2
|
import eu.darken.capod.common.uix.Activity2
|
||||||
@@ -29,6 +30,11 @@ import eu.darken.capod.main.core.currentThemeState
|
|||||||
import eu.darken.capod.main.core.themeState
|
import eu.darken.capod.main.core.themeState
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private sealed interface RecorderDialog {
|
||||||
|
data object SentConfirm : RecorderDialog
|
||||||
|
data object DeleteConfirm : RecorderDialog
|
||||||
|
}
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class RecorderActivity : Activity2() {
|
class RecorderActivity : Activity2() {
|
||||||
|
|
||||||
@@ -58,6 +64,7 @@ class RecorderActivity : Activity2() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var hasShared by remember { mutableStateOf(false) }
|
var hasShared by remember { mutableStateOf(false) }
|
||||||
|
var dialog by remember { mutableStateOf<RecorderDialog?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
vm.events.collect { event ->
|
vm.events.collect { event ->
|
||||||
@@ -71,50 +78,44 @@ class RecorderActivity : Activity2() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var showSentConfirm by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
LifecycleResumeEffect(hasShared) {
|
LifecycleResumeEffect(hasShared) {
|
||||||
if (hasShared) {
|
if (hasShared) {
|
||||||
showSentConfirm = true
|
dialog = RecorderDialog.SentConfirm
|
||||||
hasShared = false
|
hasShared = false
|
||||||
}
|
}
|
||||||
onPauseOrDispose {}
|
onPauseOrDispose {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showSentConfirm) {
|
when (dialog) {
|
||||||
LaunchedEffect(Unit) {
|
is RecorderDialog.SentConfirm -> {
|
||||||
MaterialAlertDialogBuilder(this@RecorderActivity).apply {
|
ConfirmationDialog(
|
||||||
setTitle(R.string.support_debuglog_sent_title)
|
title = stringResource(R.string.support_debuglog_sent_title),
|
||||||
setMessage(R.string.support_debuglog_sent_message)
|
message = stringResource(R.string.support_debuglog_sent_message),
|
||||||
setPositiveButton(R.string.general_done_action) { _, _ ->
|
confirmLabel = stringResource(R.string.general_done_action),
|
||||||
showSentConfirm = false
|
dismissLabel = stringResource(R.string.general_cancel_action),
|
||||||
|
onConfirm = {
|
||||||
|
dialog = null
|
||||||
vm.discard()
|
vm.discard()
|
||||||
}
|
},
|
||||||
setNegativeButton(R.string.general_cancel_action) { _, _ ->
|
onDismiss = { dialog = null },
|
||||||
showSentConfirm = false
|
)
|
||||||
}
|
|
||||||
setOnCancelListener { showSentConfirm = false }
|
|
||||||
}.show()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var showDeleteConfirm by remember { mutableStateOf(false) }
|
is RecorderDialog.DeleteConfirm -> {
|
||||||
|
ConfirmationDialog(
|
||||||
if (showDeleteConfirm) {
|
title = stringResource(R.string.support_debuglog_session_delete_title),
|
||||||
LaunchedEffect(Unit) {
|
message = stringResource(R.string.support_debuglog_session_delete_message),
|
||||||
MaterialAlertDialogBuilder(this@RecorderActivity).apply {
|
confirmLabel = stringResource(R.string.profiles_delete_action),
|
||||||
setTitle(R.string.support_debuglog_session_delete_title)
|
dismissLabel = stringResource(R.string.general_cancel_action),
|
||||||
setMessage(R.string.support_debuglog_session_delete_message)
|
onConfirm = {
|
||||||
setPositiveButton(R.string.profiles_delete_action) { _, _ ->
|
dialog = null
|
||||||
showDeleteConfirm = false
|
|
||||||
vm.discard()
|
vm.discard()
|
||||||
}
|
},
|
||||||
setNegativeButton(R.string.general_cancel_action) { _, _ ->
|
onDismiss = { dialog = null },
|
||||||
showDeleteConfirm = false
|
)
|
||||||
}
|
|
||||||
setOnCancelListener { showDeleteConfirm = false }
|
|
||||||
}.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
null -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||||
@@ -123,7 +124,7 @@ class RecorderActivity : Activity2() {
|
|||||||
state = it,
|
state = it,
|
||||||
onShare = { vm.share() },
|
onShare = { vm.share() },
|
||||||
onKeep = { vm.keep() },
|
onKeep = { vm.keep() },
|
||||||
onDiscard = { showDeleteConfirm = true },
|
onDiscard = { dialog = RecorderDialog.DeleteConfirm },
|
||||||
onPrivacyPolicy = { vm.goPrivacyPolicy() },
|
onPrivacyPolicy = { vm.goPrivacyPolicy() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-18
@@ -1,24 +1,55 @@
|
|||||||
package eu.darken.capod.common.debug.recording.ui
|
package eu.darken.capod.common.debug.recording.ui
|
||||||
|
|
||||||
import android.content.Context
|
import androidx.compose.foundation.layout.Column
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
import eu.darken.capod.common.PrivacyPolicy
|
|
||||||
import eu.darken.capod.common.WebpageTool
|
|
||||||
|
|
||||||
class RecorderConsentDialog(
|
@Composable
|
||||||
private val context: Context,
|
fun RecorderConsentDialog(
|
||||||
private val webpageTool: WebpageTool
|
onStartRecord: () -> Unit,
|
||||||
|
onOpenPrivacyPolicy: () -> Unit,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
fun showDialog(onStartRecord: () -> Unit) {
|
AlertDialog(
|
||||||
MaterialAlertDialogBuilder(context).apply {
|
onDismissRequest = onDismiss,
|
||||||
setTitle(R.string.support_debuglog_label)
|
title = { Text(text = stringResource(R.string.support_debuglog_label)) },
|
||||||
setMessage(R.string.settings_debuglog_explanation)
|
text = {
|
||||||
setPositiveButton(R.string.debug_debuglog_record_action) { _, _ -> onStartRecord() }
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
setNegativeButton(R.string.general_cancel_action) { _, _ -> }
|
Text(text = stringResource(R.string.settings_debuglog_explanation))
|
||||||
setNeutralButton(R.string.settings_privacy_policy_label) { _, _ ->
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
webpageTool.open(PrivacyPolicy.URL)
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onOpenPrivacyPolicy()
|
||||||
|
onDismiss()
|
||||||
|
},
|
||||||
|
modifier = Modifier.align(Alignment.End),
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.settings_privacy_policy_label))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.show()
|
},
|
||||||
}
|
confirmButton = {
|
||||||
}
|
TextButton(onClick = {
|
||||||
|
onDismiss()
|
||||||
|
onStartRecord()
|
||||||
|
}) {
|
||||||
|
Text(text = stringResource(R.string.debug_debuglog_record_action))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text(text = stringResource(R.string.general_cancel_action))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
package eu.darken.capod.common.error
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import eu.darken.capod.R
|
|
||||||
|
|
||||||
fun Throwable.asErrorDialogBuilder(
|
|
||||||
context: Context
|
|
||||||
) = MaterialAlertDialogBuilder(context).apply {
|
|
||||||
val error = this@asErrorDialogBuilder
|
|
||||||
val localizedError = error.localized(context)
|
|
||||||
|
|
||||||
setTitle(localizedError.label)
|
|
||||||
setMessage(localizedError.description)
|
|
||||||
|
|
||||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
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.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.twotone.ArrowBack
|
import androidx.compose.material.icons.automirrored.twotone.ArrowBack
|
||||||
import androidx.compose.material.icons.automirrored.twotone.MenuBook
|
import androidx.compose.material.icons.automirrored.twotone.MenuBook
|
||||||
@@ -25,6 +24,7 @@ import androidx.compose.material.icons.twotone.Delete
|
|||||||
import androidx.compose.material.icons.twotone.FiberManualRecord
|
import androidx.compose.material.icons.twotone.FiberManualRecord
|
||||||
import androidx.compose.material.icons.twotone.Settings
|
import androidx.compose.material.icons.twotone.Settings
|
||||||
import androidx.compose.material.icons.twotone.Warning
|
import androidx.compose.material.icons.twotone.Warning
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@@ -32,6 +32,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -50,10 +51,10 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
import eu.darken.capod.common.PrivacyPolicy
|
import eu.darken.capod.common.PrivacyPolicy
|
||||||
import eu.darken.capod.common.WebpageTool
|
import eu.darken.capod.common.WebpageTool
|
||||||
|
import eu.darken.capod.common.compose.ConfirmationDialog
|
||||||
import eu.darken.capod.common.compose.Preview2
|
import eu.darken.capod.common.compose.Preview2
|
||||||
import eu.darken.capod.common.compose.PreviewWrapper
|
import eu.darken.capod.common.compose.PreviewWrapper
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
@@ -65,6 +66,13 @@ import eu.darken.capod.common.navigation.NavigationEventHandler
|
|||||||
import eu.darken.capod.common.settings.SettingsBaseItem
|
import eu.darken.capod.common.settings.SettingsBaseItem
|
||||||
import eu.darken.capod.common.settings.SettingsCategoryHeader
|
import eu.darken.capod.common.settings.SettingsCategoryHeader
|
||||||
|
|
||||||
|
private sealed interface SupportDialog {
|
||||||
|
data object Consent : SupportDialog
|
||||||
|
data object ShortRecordingWarning : SupportDialog
|
||||||
|
data class DeleteSession(val sessionId: String) : SupportDialog
|
||||||
|
data object ClearLogs : SupportDialog
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SupportScreenHost(vm: SupportViewModel = hiltViewModel()) {
|
fun SupportScreenHost(vm: SupportViewModel = hiltViewModel()) {
|
||||||
ErrorEventHandler(vm)
|
ErrorEventHandler(vm)
|
||||||
@@ -78,19 +86,17 @@ fun SupportScreenHost(vm: SupportViewModel = hiltViewModel()) {
|
|||||||
|
|
||||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||||
|
|
||||||
var showShortRecordingWarning by remember { mutableStateOf(false) }
|
var dialog by remember { mutableStateOf<SupportDialog?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
vm.events.collect { event ->
|
vm.events.collect { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
SupportViewModel.Event.ShowConsentDialog -> {
|
SupportViewModel.Event.ShowConsentDialog -> {
|
||||||
RecorderConsentDialog(context, WebpageTool(context)).showDialog {
|
dialog = SupportDialog.Consent
|
||||||
vm.startDebugLog()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SupportViewModel.Event.ShowShortRecordingWarning -> {
|
SupportViewModel.Event.ShowShortRecordingWarning -> {
|
||||||
showShortRecordingWarning = true
|
dialog = SupportDialog.ShortRecordingWarning
|
||||||
}
|
}
|
||||||
|
|
||||||
is SupportViewModel.Event.OpenRecorderActivity -> {
|
is SupportViewModel.Event.OpenRecorderActivity -> {
|
||||||
@@ -103,12 +109,69 @@ fun SupportScreenHost(vm: SupportViewModel = hiltViewModel()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showShortRecordingWarning) {
|
when (val d = dialog) {
|
||||||
ShortRecordingWarningDialog(
|
is SupportDialog.Consent -> {
|
||||||
context = context,
|
RecorderConsentDialog(
|
||||||
onDismiss = { showShortRecordingWarning = false },
|
onStartRecord = {
|
||||||
onStopAnyway = { vm.forceStopDebugLog() },
|
vm.startDebugLog()
|
||||||
)
|
},
|
||||||
|
onOpenPrivacyPolicy = {
|
||||||
|
WebpageTool(context).open(PrivacyPolicy.URL)
|
||||||
|
},
|
||||||
|
onDismiss = { dialog = null },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is SupportDialog.ShortRecordingWarning -> {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { dialog = null },
|
||||||
|
title = { Text(text = stringResource(R.string.debug_debuglog_short_recording_title)) },
|
||||||
|
text = { Text(text = stringResource(R.string.debug_debuglog_short_recording_message)) },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { dialog = null }) {
|
||||||
|
Text(text = stringResource(R.string.debug_debuglog_short_recording_continue))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
dialog = null
|
||||||
|
vm.forceStopDebugLog()
|
||||||
|
}) {
|
||||||
|
Text(text = stringResource(R.string.debug_debuglog_short_recording_stop))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is SupportDialog.DeleteSession -> {
|
||||||
|
ConfirmationDialog(
|
||||||
|
title = stringResource(R.string.support_debuglog_session_delete_title),
|
||||||
|
message = stringResource(R.string.support_debuglog_session_delete_message),
|
||||||
|
confirmLabel = stringResource(R.string.profiles_delete_action),
|
||||||
|
dismissLabel = stringResource(R.string.general_cancel_action),
|
||||||
|
onConfirm = {
|
||||||
|
dialog = null
|
||||||
|
vm.deleteSession(d.sessionId)
|
||||||
|
},
|
||||||
|
onDismiss = { dialog = null },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is SupportDialog.ClearLogs -> {
|
||||||
|
ConfirmationDialog(
|
||||||
|
title = stringResource(R.string.support_debuglog_session_delete_title),
|
||||||
|
message = stringResource(R.string.support_debuglog_clear_all_message),
|
||||||
|
confirmLabel = stringResource(R.string.profiles_delete_action),
|
||||||
|
dismissLabel = stringResource(R.string.general_cancel_action),
|
||||||
|
onConfirm = {
|
||||||
|
dialog = null
|
||||||
|
vm.clearDebugLogs()
|
||||||
|
},
|
||||||
|
onDismiss = { dialog = null },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
null -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
state?.let {
|
state?.let {
|
||||||
@@ -122,53 +185,13 @@ fun SupportScreenHost(vm: SupportViewModel = hiltViewModel()) {
|
|||||||
onTroubleShooter = { vm.goToTroubleShooter() },
|
onTroubleShooter = { vm.goToTroubleShooter() },
|
||||||
onDebugLogToggle = { vm.onDebugLogToggle() },
|
onDebugLogToggle = { vm.onDebugLogToggle() },
|
||||||
onOpenSession = { vm.openSession(it) },
|
onOpenSession = { vm.openSession(it) },
|
||||||
onDeleteSession = { id ->
|
onDeleteSession = { id -> dialog = SupportDialog.DeleteSession(id) },
|
||||||
MaterialAlertDialogBuilder(context).apply {
|
|
||||||
setTitle(R.string.support_debuglog_session_delete_title)
|
|
||||||
setMessage(R.string.support_debuglog_session_delete_message)
|
|
||||||
setPositiveButton(R.string.profiles_delete_action) { _, _ ->
|
|
||||||
vm.deleteSession(id)
|
|
||||||
}
|
|
||||||
setNegativeButton(R.string.general_cancel_action) { _, _ -> }
|
|
||||||
}.show()
|
|
||||||
},
|
|
||||||
onStopRecording = { vm.onDebugLogToggle() },
|
onStopRecording = { vm.onDebugLogToggle() },
|
||||||
onClearLogs = {
|
onClearLogs = { dialog = SupportDialog.ClearLogs },
|
||||||
MaterialAlertDialogBuilder(context).apply {
|
|
||||||
setTitle(R.string.support_debuglog_session_delete_title)
|
|
||||||
setMessage(R.string.support_debuglog_clear_all_message)
|
|
||||||
setPositiveButton(R.string.profiles_delete_action) { _, _ ->
|
|
||||||
vm.clearDebugLogs()
|
|
||||||
}
|
|
||||||
setNegativeButton(R.string.general_cancel_action) { _, _ -> }
|
|
||||||
}.show()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ShortRecordingWarningDialog(
|
|
||||||
context: android.content.Context,
|
|
||||||
onDismiss: () -> Unit,
|
|
||||||
onStopAnyway: () -> Unit,
|
|
||||||
) {
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
MaterialAlertDialogBuilder(context).apply {
|
|
||||||
setTitle(R.string.debug_debuglog_short_recording_title)
|
|
||||||
setMessage(R.string.debug_debuglog_short_recording_message)
|
|
||||||
setPositiveButton(R.string.debug_debuglog_short_recording_continue) { _, _ ->
|
|
||||||
onDismiss()
|
|
||||||
}
|
|
||||||
setNegativeButton(R.string.debug_debuglog_short_recording_stop) { _, _ ->
|
|
||||||
onDismiss()
|
|
||||||
onStopAnyway()
|
|
||||||
}
|
|
||||||
setOnCancelListener { onDismiss() }
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SupportScreen(
|
fun SupportScreen(
|
||||||
state: SupportViewModel.State,
|
state: SupportViewModel.State,
|
||||||
|
|||||||
+78
-50
@@ -23,6 +23,7 @@ import androidx.compose.material.icons.twotone.BugReport
|
|||||||
import androidx.compose.material.icons.twotone.Cancel
|
import androidx.compose.material.icons.twotone.Cancel
|
||||||
import androidx.compose.material.icons.twotone.Delete
|
import androidx.compose.material.icons.twotone.Delete
|
||||||
import androidx.compose.material.icons.twotone.Email
|
import androidx.compose.material.icons.twotone.Email
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
@@ -37,6 +38,7 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -55,15 +57,23 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.LifecycleResumeEffect
|
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
|
import eu.darken.capod.common.PrivacyPolicy
|
||||||
import eu.darken.capod.common.WebpageTool
|
import eu.darken.capod.common.WebpageTool
|
||||||
|
import eu.darken.capod.common.compose.ConfirmationDialog
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import eu.darken.capod.common.debug.recording.ui.RecorderConsentDialog
|
import eu.darken.capod.common.debug.recording.ui.RecorderConsentDialog
|
||||||
import eu.darken.capod.common.error.ErrorEventHandler
|
import eu.darken.capod.common.error.ErrorEventHandler
|
||||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||||
import eu.darken.capod.main.ui.settings.support.contactform.ContactFormViewModel.Category
|
import eu.darken.capod.main.ui.settings.support.contactform.ContactFormViewModel.Category
|
||||||
|
|
||||||
|
private sealed interface ContactFormDialog {
|
||||||
|
data object SentConfirm : ContactFormDialog
|
||||||
|
data object Consent : ContactFormDialog
|
||||||
|
data object ShortRecordingWarning : ContactFormDialog
|
||||||
|
data class DeleteSession(val sessionId: String) : ContactFormDialog
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ContactFormScreenHost(vm: ContactFormViewModel = hiltViewModel()) {
|
fun ContactFormScreenHost(vm: ContactFormViewModel = hiltViewModel()) {
|
||||||
ErrorEventHandler(vm)
|
ErrorEventHandler(vm)
|
||||||
@@ -73,36 +83,17 @@ fun ContactFormScreenHost(vm: ContactFormViewModel = hiltViewModel()) {
|
|||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
var hasSentEmail by remember { mutableStateOf(false) }
|
var hasSentEmail by remember { mutableStateOf(false) }
|
||||||
var showSentConfirm by remember { mutableStateOf(false) }
|
var dialog by remember { mutableStateOf<ContactFormDialog?>(null) }
|
||||||
|
|
||||||
LifecycleResumeEffect(hasSentEmail) {
|
LifecycleResumeEffect(hasSentEmail) {
|
||||||
vm.refreshLogSessions()
|
vm.refreshLogSessions()
|
||||||
if (hasSentEmail) {
|
if (hasSentEmail) {
|
||||||
showSentConfirm = true
|
dialog = ContactFormDialog.SentConfirm
|
||||||
hasSentEmail = false
|
hasSentEmail = false
|
||||||
}
|
}
|
||||||
onPauseOrDispose {}
|
onPauseOrDispose {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showSentConfirm) {
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
MaterialAlertDialogBuilder(context).apply {
|
|
||||||
setTitle(R.string.support_contact_sent_title)
|
|
||||||
setMessage(R.string.support_contact_sent_message)
|
|
||||||
setPositiveButton(R.string.general_done_action) { _, _ ->
|
|
||||||
showSentConfirm = false
|
|
||||||
vm.confirmSent()
|
|
||||||
}
|
|
||||||
setNegativeButton(R.string.general_cancel_action) { _, _ ->
|
|
||||||
showSentConfirm = false
|
|
||||||
}
|
|
||||||
setOnCancelListener { showSentConfirm = false }
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var showShortRecordingWarning by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
vm.events.collect { event ->
|
vm.events.collect { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
@@ -123,33 +114,79 @@ fun ContactFormScreenHost(vm: ContactFormViewModel = hiltViewModel()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ContactFormViewModel.Event.ShowConsentDialog -> {
|
ContactFormViewModel.Event.ShowConsentDialog -> {
|
||||||
RecorderConsentDialog(context, WebpageTool(context)).showDialog {
|
dialog = ContactFormDialog.Consent
|
||||||
vm.doStartRecording()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ContactFormViewModel.Event.ShowShortRecordingWarning -> {
|
ContactFormViewModel.Event.ShowShortRecordingWarning -> {
|
||||||
showShortRecordingWarning = true
|
dialog = ContactFormDialog.ShortRecordingWarning
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showShortRecordingWarning) {
|
when (val d = dialog) {
|
||||||
LaunchedEffect(Unit) {
|
is ContactFormDialog.SentConfirm -> {
|
||||||
MaterialAlertDialogBuilder(context).apply {
|
ConfirmationDialog(
|
||||||
setTitle(R.string.debug_debuglog_short_recording_title)
|
title = stringResource(R.string.support_contact_sent_title),
|
||||||
setMessage(R.string.debug_debuglog_short_recording_message)
|
message = stringResource(R.string.support_contact_sent_message),
|
||||||
setPositiveButton(R.string.debug_debuglog_short_recording_continue) { _, _ ->
|
confirmLabel = stringResource(R.string.general_done_action),
|
||||||
showShortRecordingWarning = false
|
dismissLabel = stringResource(R.string.general_cancel_action),
|
||||||
}
|
onConfirm = {
|
||||||
setNegativeButton(R.string.debug_debuglog_short_recording_stop) { _, _ ->
|
dialog = null
|
||||||
showShortRecordingWarning = false
|
vm.confirmSent()
|
||||||
vm.forceStopRecording()
|
},
|
||||||
}
|
onDismiss = { dialog = null },
|
||||||
setOnCancelListener { showShortRecordingWarning = false }
|
)
|
||||||
}.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ContactFormDialog.Consent -> {
|
||||||
|
RecorderConsentDialog(
|
||||||
|
onStartRecord = {
|
||||||
|
vm.doStartRecording()
|
||||||
|
},
|
||||||
|
onOpenPrivacyPolicy = {
|
||||||
|
WebpageTool(context).open(PrivacyPolicy.URL)
|
||||||
|
},
|
||||||
|
onDismiss = { dialog = null },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ContactFormDialog.ShortRecordingWarning -> {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { dialog = null },
|
||||||
|
title = { Text(text = stringResource(R.string.debug_debuglog_short_recording_title)) },
|
||||||
|
text = { Text(text = stringResource(R.string.debug_debuglog_short_recording_message)) },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { dialog = null }) {
|
||||||
|
Text(text = stringResource(R.string.debug_debuglog_short_recording_continue))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = {
|
||||||
|
dialog = null
|
||||||
|
vm.forceStopRecording()
|
||||||
|
}) {
|
||||||
|
Text(text = stringResource(R.string.debug_debuglog_short_recording_stop))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ContactFormDialog.DeleteSession -> {
|
||||||
|
ConfirmationDialog(
|
||||||
|
title = stringResource(R.string.support_contact_debuglog_delete_title),
|
||||||
|
message = stringResource(R.string.support_contact_debuglog_delete_message),
|
||||||
|
confirmLabel = stringResource(R.string.profiles_delete_action),
|
||||||
|
dismissLabel = stringResource(R.string.general_cancel_action),
|
||||||
|
onConfirm = {
|
||||||
|
dialog = null
|
||||||
|
vm.deleteLogSession(d.sessionId)
|
||||||
|
},
|
||||||
|
onDismiss = { dialog = null },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
null -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||||
@@ -162,16 +199,7 @@ fun ContactFormScreenHost(vm: ContactFormViewModel = hiltViewModel()) {
|
|||||||
onDescriptionChange = { vm.updateDescription(it) },
|
onDescriptionChange = { vm.updateDescription(it) },
|
||||||
onExpectedChange = { vm.updateExpectedBehavior(it) },
|
onExpectedChange = { vm.updateExpectedBehavior(it) },
|
||||||
onSelectSession = { vm.selectLogSession(it) },
|
onSelectSession = { vm.selectLogSession(it) },
|
||||||
onDeleteSession = { id ->
|
onDeleteSession = { id -> dialog = ContactFormDialog.DeleteSession(id) },
|
||||||
MaterialAlertDialogBuilder(context).apply {
|
|
||||||
setTitle(R.string.support_contact_debuglog_delete_title)
|
|
||||||
setMessage(R.string.support_contact_debuglog_delete_message)
|
|
||||||
setPositiveButton(R.string.profiles_delete_action) { _, _ ->
|
|
||||||
vm.deleteLogSession(id)
|
|
||||||
}
|
|
||||||
setNegativeButton(R.string.general_cancel_action) { _, _ -> }
|
|
||||||
}.show()
|
|
||||||
},
|
|
||||||
onStartRecording = { vm.startRecording() },
|
onStartRecording = { vm.startRecording() },
|
||||||
onStopRecording = { vm.stopRecording() },
|
onStopRecording = { vm.stopRecording() },
|
||||||
onSend = { vm.send() },
|
onSend = { vm.send() },
|
||||||
|
|||||||
Reference in New Issue
Block a user