mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat: consolidate dialogs (#4506)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
7bcc51863f
commit
ea6d1ffa32
59 changed files with 2042 additions and 1659 deletions
|
|
@ -28,10 +28,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import androidx.appcompat.app.AppCompatActivity.RESULT_OK
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
|
|
@ -46,13 +43,7 @@ import androidx.compose.material.icons.rounded.LocationOn
|
|||
import androidx.compose.material.icons.rounded.Memory
|
||||
import androidx.compose.material.icons.rounded.Output
|
||||
import androidx.compose.material.icons.rounded.WavingHand
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -107,6 +98,7 @@ import org.meshtastic.core.strings.use_homoglyph_characters_encoding
|
|||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.core.ui.component.SwitchListItem
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.core.ui.theme.MODE_DYNAMIC
|
||||
|
|
@ -501,14 +493,20 @@ private fun AppVersionButton(
|
|||
|
||||
@Composable
|
||||
private fun LanguagePickerDialog(onDismiss: () -> Unit) {
|
||||
SettingsDialog(title = stringResource(Res.string.preferences_language), onDismiss = onDismiss) {
|
||||
languageMap().forEach { (languageTag, languageName) ->
|
||||
ListItem(text = languageName, trailingIcon = null) {
|
||||
LanguageUtils.setAppLocale(languageTag)
|
||||
onDismiss()
|
||||
MeshtasticDialog(
|
||||
title = stringResource(Res.string.preferences_language),
|
||||
onDismiss = onDismiss,
|
||||
text = {
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||
languageMap().forEach { (languageTag, languageName) ->
|
||||
ListItem(text = languageName, trailingIcon = null) {
|
||||
LanguageUtils.setAppLocale(languageTag)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private enum class ThemeOption(val label: StringResource, val mode: Int) {
|
||||
|
|
@ -520,35 +518,18 @@ private enum class ThemeOption(val label: StringResource, val mode: Int) {
|
|||
|
||||
@Composable
|
||||
private fun ThemePickerDialog(onClickTheme: (Int) -> Unit, onDismiss: () -> Unit) {
|
||||
SettingsDialog(title = stringResource(Res.string.choose_theme), onDismiss = onDismiss) {
|
||||
ThemeOption.entries.forEach { option ->
|
||||
ListItem(text = stringResource(option.label), trailingIcon = null) {
|
||||
onClickTheme(option.mode)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun SettingsDialog(title: String, onDismiss: () -> Unit, content: @Composable ColumnScope.() -> Unit) {
|
||||
BasicAlertDialog(onDismissRequest = onDismiss) {
|
||||
Surface(
|
||||
modifier = Modifier.wrapContentWidth().wrapContentHeight(),
|
||||
shape = MaterialTheme.shapes.large,
|
||||
color = AlertDialogDefaults.containerColor,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation,
|
||||
) {
|
||||
MeshtasticDialog(
|
||||
title = stringResource(Res.string.choose_theme),
|
||||
onDismiss = onDismiss,
|
||||
text = {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 8.dp),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) { content() }
|
||||
ThemeOption.entries.forEach { option ->
|
||||
ListItem(text = stringResource(option.label), trailingIcon = null) {
|
||||
onClickTheme(option.mode)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ import org.jetbrains.compose.resources.pluralStringResource
|
|||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.debug_clear
|
||||
import org.meshtastic.core.strings.debug_clear_logs_confirm
|
||||
import org.meshtastic.core.strings.debug_decoded_payload
|
||||
import org.meshtastic.core.strings.debug_default_search
|
||||
import org.meshtastic.core.strings.debug_export_failed
|
||||
|
|
@ -103,7 +102,6 @@ import org.meshtastic.core.strings.log_retention_never
|
|||
import org.meshtastic.core.ui.component.CopyIconButton
|
||||
import org.meshtastic.core.ui.component.DropDownPreference
|
||||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.component.SimpleAlertDialog
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.theme.AnnotationColor
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
|
@ -178,7 +176,7 @@ fun DebugScreen(onNavigateUp: () -> Unit, viewModel: DebugViewModel = hiltViewMo
|
|||
IconButton(onClick = { showSettings = !showSettings }) {
|
||||
Icon(imageVector = Icons.Rounded.Settings, contentDescription = null)
|
||||
}
|
||||
DebugMenuActions(deleteLogs = { viewModel.deleteAllLogs() })
|
||||
DebugMenuActions(deleteLogs = { viewModel.requestDeleteAllLogs() })
|
||||
},
|
||||
onClickChip = {},
|
||||
)
|
||||
|
|
@ -413,22 +411,9 @@ private fun rememberAnnotatedLogMessage(log: UiMeshLog, searchText: String): Ann
|
|||
|
||||
@Composable
|
||||
fun DebugMenuActions(deleteLogs: () -> Unit, modifier: Modifier = Modifier) {
|
||||
var showDeleteLogsDialog by remember { mutableStateOf(false) }
|
||||
|
||||
IconButton(onClick = { showDeleteLogsDialog = true }, modifier = modifier.padding(4.dp)) {
|
||||
IconButton(onClick = deleteLogs, modifier = modifier.padding(4.dp)) {
|
||||
Icon(imageVector = Icons.Rounded.Delete, contentDescription = stringResource(Res.string.debug_clear))
|
||||
}
|
||||
if (showDeleteLogsDialog) {
|
||||
SimpleAlertDialog(
|
||||
title = Res.string.debug_clear,
|
||||
text = Res.string.debug_clear_logs_confirm,
|
||||
onConfirm = {
|
||||
showDeleteLogsDialog = false
|
||||
deleteLogs()
|
||||
},
|
||||
onDismiss = { showDeleteLogsDialog = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun exportAllLogsToUri(context: Context, targetUri: Uri, logs: List<UiMeshLog>) =
|
||||
|
|
@ -487,7 +472,16 @@ private fun DecodedPayloadBlock(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val commonTextStyle =
|
||||
TextStyle(fontSize = if (isSelected) 10.sp else 8.sp, fontWeight = FontWeight.Bold, color = colorScheme.primary)
|
||||
TextStyle(
|
||||
fontSize =
|
||||
if (isSelected) {
|
||||
10.sp
|
||||
} else {
|
||||
8.sp
|
||||
},
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = colorScheme.primary,
|
||||
)
|
||||
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ import org.meshtastic.core.model.getTracerouteResponse
|
|||
import org.meshtastic.core.model.util.decodeOrNull
|
||||
import org.meshtastic.core.model.util.toReadableString
|
||||
import org.meshtastic.core.prefs.meshlog.MeshLogPrefs
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.debug_clear
|
||||
import org.meshtastic.core.strings.debug_clear_logs_confirm
|
||||
import org.meshtastic.core.ui.util.AlertManager
|
||||
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
import org.meshtastic.proto.AdminMessage
|
||||
import org.meshtastic.proto.MeshPacket
|
||||
|
|
@ -215,6 +219,7 @@ constructor(
|
|||
private val meshLogRepository: MeshLogRepository,
|
||||
private val nodeRepository: NodeRepository,
|
||||
private val meshLogPrefs: MeshLogPrefs,
|
||||
private val alertManager: AlertManager,
|
||||
) : ViewModel() {
|
||||
|
||||
val meshLog: StateFlow<ImmutableList<UiMeshLog>> =
|
||||
|
|
@ -393,6 +398,14 @@ constructor(
|
|||
|
||||
private fun Int.asNodeId(): String = "!%08x".format(Locale.getDefault(), this)
|
||||
|
||||
fun requestDeleteAllLogs() {
|
||||
alertManager.showAlert(
|
||||
titleRes = Res.string.debug_clear,
|
||||
messageRes = Res.string.debug_clear_logs_confirm,
|
||||
onConfirm = { deleteAllLogs() },
|
||||
)
|
||||
}
|
||||
|
||||
fun deleteAllLogs() = viewModelScope.launch(Dispatchers.IO) { meshLogRepository.deleteAll() }
|
||||
|
||||
@Immutable
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.settings.radio
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
|
@ -27,20 +26,15 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -48,9 +42,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.database.entity.NodeEntity
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.are_you_sure
|
||||
import org.meshtastic.core.strings.cancel
|
||||
import org.meshtastic.core.strings.clean_node_database_confirmation
|
||||
import org.meshtastic.core.strings.clean_node_database_description
|
||||
import org.meshtastic.core.strings.clean_node_database_title
|
||||
import org.meshtastic.core.strings.clean_nodes_older_than
|
||||
|
|
@ -68,21 +59,9 @@ fun CleanNodeDatabaseScreen(viewModel: CleanNodeDatabaseViewModel = hiltViewMode
|
|||
val olderThanDays by viewModel.olderThanDays.collectAsState()
|
||||
val onlyUnknownNodes by viewModel.onlyUnknownNodes.collectAsState()
|
||||
val nodesToDelete by viewModel.nodesToDelete.collectAsState()
|
||||
var showConfirmationDialog by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(olderThanDays, onlyUnknownNodes) { viewModel.getNodesToDelete() }
|
||||
|
||||
if (showConfirmationDialog) {
|
||||
ConfirmationDialog(
|
||||
nodesToDeleteCount = nodesToDelete.size,
|
||||
onConfirm = {
|
||||
viewModel.cleanNodes()
|
||||
showConfirmationDialog = false
|
||||
},
|
||||
onDismiss = { showConfirmationDialog = false },
|
||||
)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp).verticalScroll(rememberScrollState())) {
|
||||
Text(stringResource(Res.string.clean_node_database_title))
|
||||
Text(stringResource(Res.string.clean_node_database_description), style = MaterialTheme.typography.bodySmall)
|
||||
|
|
@ -105,7 +84,7 @@ fun CleanNodeDatabaseScreen(viewModel: CleanNodeDatabaseViewModel = hiltViewMode
|
|||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Button(
|
||||
onClick = { if (nodesToDelete.isNotEmpty()) showConfirmationDialog = true },
|
||||
onClick = { if (nodesToDelete.isNotEmpty()) viewModel.requestCleanNodes() },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = nodesToDelete.isNotEmpty(),
|
||||
) {
|
||||
|
|
@ -186,21 +165,3 @@ private fun NodesDeletionPreview(nodesToDelete: List<NodeEntity>) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable for the confirmation dialog before deleting nodes.
|
||||
*
|
||||
* @param nodesToDeleteCount The number of nodes to be deleted.
|
||||
* @param onConfirm Callback for when the user confirms the deletion.
|
||||
* @param onDismiss Callback for when the user dismisses the dialog.
|
||||
*/
|
||||
@Composable
|
||||
private fun ConfirmationDialog(nodesToDeleteCount: Int, onConfirm: () -> Unit, onDismiss: () -> Unit) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(Res.string.are_you_sure)) },
|
||||
text = { Text(stringResource(Res.string.clean_node_database_confirmation, nodesToDeleteCount)) },
|
||||
confirmButton = { Button(onClick = onConfirm) { Text(stringResource(Res.string.clean_now)) } },
|
||||
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(Res.string.cancel)) } },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.settings.radio
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
|
@ -23,9 +22,15 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import org.meshtastic.core.data.repository.NodeRepository
|
||||
import org.meshtastic.core.database.entity.NodeEntity
|
||||
import org.meshtastic.core.service.ServiceRepository
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.are_you_sure
|
||||
import org.meshtastic.core.strings.clean_node_database_confirmation
|
||||
import org.meshtastic.core.strings.clean_now
|
||||
import org.meshtastic.core.ui.util.AlertManager
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
|
@ -42,6 +47,7 @@ class CleanNodeDatabaseViewModel
|
|||
constructor(
|
||||
private val nodeRepository: NodeRepository,
|
||||
private val serviceRepository: ServiceRepository,
|
||||
private val alertManager: AlertManager,
|
||||
) : ViewModel() {
|
||||
private val _olderThanDays = MutableStateFlow(30f)
|
||||
val olderThanDays = _olderThanDays.asStateFlow()
|
||||
|
|
@ -100,6 +106,19 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun requestCleanNodes() {
|
||||
viewModelScope.launch {
|
||||
val count = _nodesToDelete.value.size
|
||||
val message = getString(Res.string.clean_node_database_confirmation, count)
|
||||
alertManager.showAlert(
|
||||
titleRes = Res.string.are_you_sure,
|
||||
message = message,
|
||||
confirmTextRes = Res.string.clean_now,
|
||||
onConfirm = { cleanNodes() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the nodes currently queued in [_nodesToDelete] from the database and instructs the mesh service to remove
|
||||
* them.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import androidx.compose.foundation.clickable
|
|||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
|
|
@ -30,12 +29,10 @@ import androidx.compose.material.icons.filled.CloudDownload
|
|||
import androidx.compose.material.icons.filled.CloudUpload
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.LocationOn
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
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
|
||||
|
|
@ -62,6 +59,7 @@ import org.meshtastic.core.strings.secondary_no_telemetry
|
|||
import org.meshtastic.core.strings.security_icon_help_dismiss
|
||||
import org.meshtastic.core.strings.uplink_enabled
|
||||
import org.meshtastic.core.strings.uplink_feature_description
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
|
||||
@Composable
|
||||
internal fun ChannelLegend(onClick: () -> Unit) {
|
||||
|
|
@ -109,10 +107,10 @@ internal enum class ChannelIcons(
|
|||
|
||||
@Composable
|
||||
internal fun ChannelLegendDialog(capabilities: Capabilities, onDismiss: () -> Unit) {
|
||||
AlertDialog(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(Res.string.channel_features)) },
|
||||
MeshtasticDialog(
|
||||
onDismiss = onDismiss,
|
||||
title = stringResource(Res.string.channel_features),
|
||||
dismissText = stringResource(Res.string.security_icon_help_dismiss),
|
||||
text = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
|
|
@ -152,15 +150,6 @@ internal fun ChannelLegendDialog(capabilities: Capabilities, onDismiss: () -> Un
|
|||
IconDefinitions()
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(Res.string.security_icon_help_dismiss)) }
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,19 +16,11 @@
|
|||
*/
|
||||
package org.meshtastic.feature.settings.radio.channel.component
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -51,6 +43,7 @@ import org.meshtastic.core.strings.save
|
|||
import org.meshtastic.core.strings.uplink_enabled
|
||||
import org.meshtastic.core.ui.component.EditBase64Preference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.core.ui.component.PositionPrecisionPreference
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.proto.ChannelSettings
|
||||
|
|
@ -70,9 +63,13 @@ fun EditChannelDialog(
|
|||
|
||||
var channelInput by remember(channelSettings) { mutableStateOf(channelSettings) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
MeshtasticDialog(
|
||||
onDismiss = onDismissRequest,
|
||||
dismissText = stringResource(Res.string.cancel),
|
||||
confirmText = stringResource(Res.string.save),
|
||||
onConfirm = { onAddClick(channelInput) },
|
||||
modifier = modifier,
|
||||
title = "", // Title is handled internally by specific items if needed, or we could add one
|
||||
text = {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
EditTextPreference(
|
||||
|
|
@ -143,19 +140,6 @@ fun EditChannelDialog(
|
|||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
FlowRow(
|
||||
modifier = modifier.fillMaxWidth().padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
TextButton(modifier = modifier.weight(1f), onClick = onDismissRequest) {
|
||||
Text(stringResource(Res.string.cancel))
|
||||
}
|
||||
Button(modifier = modifier.weight(1f), onClick = { onAddClick(channelInput) }, enabled = true) {
|
||||
Text(stringResource(Res.string.save))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,26 +16,14 @@
|
|||
*/
|
||||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
|
|
@ -49,6 +37,7 @@ import org.meshtastic.core.strings.module_settings
|
|||
import org.meshtastic.core.strings.radio_configuration
|
||||
import org.meshtastic.core.strings.save
|
||||
import org.meshtastic.core.strings.short_name
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.proto.DeviceProfile
|
||||
|
||||
|
|
@ -61,8 +50,7 @@ private enum class ProfileField(val tag: Int, val labelRes: StringResource) {
|
|||
FIXED_POSITION(6, Res.string.fixed_position),
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
fun EditDeviceProfileDialog(
|
||||
title: String,
|
||||
|
|
@ -88,20 +76,36 @@ fun EditDeviceProfileDialog(
|
|||
}
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
text = {
|
||||
Column(modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = title,
|
||||
style =
|
||||
MaterialTheme.typography.titleLarge.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp),
|
||||
MeshtasticDialog(
|
||||
title = title,
|
||||
onDismiss = onDismiss,
|
||||
dismissText = stringResource(Res.string.cancel),
|
||||
confirmText = stringResource(Res.string.save),
|
||||
onConfirm = {
|
||||
val result =
|
||||
DeviceProfile(
|
||||
long_name = if (state[ProfileField.LONG_NAME] == true) deviceProfile.long_name else null,
|
||||
short_name = if (state[ProfileField.SHORT_NAME] == true) deviceProfile.short_name else null,
|
||||
channel_url = if (state[ProfileField.CHANNEL_URL] == true) deviceProfile.channel_url else null,
|
||||
config = if (state[ProfileField.CONFIG] == true) deviceProfile.config else null,
|
||||
module_config =
|
||||
if (state[ProfileField.MODULE_CONFIG] == true) {
|
||||
deviceProfile.module_config
|
||||
} else {
|
||||
null
|
||||
},
|
||||
fixed_position =
|
||||
if (state[ProfileField.FIXED_POSITION] == true) {
|
||||
deviceProfile.fixed_position
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
onConfirm(result)
|
||||
},
|
||||
modifier = modifier,
|
||||
text = {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
HorizontalDivider()
|
||||
ProfileField.entries.forEach { field ->
|
||||
val isAvailable =
|
||||
|
|
@ -124,47 +128,6 @@ fun EditDeviceProfileDialog(
|
|||
HorizontalDivider()
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
FlowRow(
|
||||
modifier = modifier.fillMaxWidth().padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
TextButton(modifier = modifier.weight(1f), onClick = onDismiss) {
|
||||
Text(stringResource(Res.string.cancel))
|
||||
}
|
||||
Button(
|
||||
modifier = modifier.weight(1f),
|
||||
onClick = {
|
||||
val result =
|
||||
DeviceProfile(
|
||||
long_name =
|
||||
if (state[ProfileField.LONG_NAME] == true) deviceProfile.long_name else null,
|
||||
short_name =
|
||||
if (state[ProfileField.SHORT_NAME] == true) deviceProfile.short_name else null,
|
||||
channel_url =
|
||||
if (state[ProfileField.CHANNEL_URL] == true) deviceProfile.channel_url else null,
|
||||
config = if (state[ProfileField.CONFIG] == true) deviceProfile.config else null,
|
||||
module_config =
|
||||
if (state[ProfileField.MODULE_CONFIG] == true) {
|
||||
deviceProfile.module_config
|
||||
} else {
|
||||
null
|
||||
},
|
||||
fixed_position =
|
||||
if (state[ProfileField.FIXED_POSITION] == true) {
|
||||
deviceProfile.fixed_position
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
onConfirm(result)
|
||||
},
|
||||
enabled = state.values.any { it },
|
||||
) {
|
||||
Text(stringResource(Res.string.save))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,12 +21,10 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -80,7 +78,7 @@ import org.meshtastic.core.ui.component.EditIPv4Preference
|
|||
import org.meshtastic.core.ui.component.EditPasswordPreference
|
||||
import org.meshtastic.core.ui.component.EditTextPreference
|
||||
import org.meshtastic.core.ui.component.ListItem
|
||||
import org.meshtastic.core.ui.component.SimpleAlertDialog
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.core.ui.util.openNfcSettings
|
||||
|
|
@ -89,7 +87,7 @@ import org.meshtastic.proto.Config
|
|||
|
||||
@Composable
|
||||
private fun ScanErrorDialog(onDismiss: () -> Unit = {}) =
|
||||
SimpleAlertDialog(title = Res.string.error, text = Res.string.wifi_qr_code_error, onDismiss = onDismiss)
|
||||
MeshtasticDialog(titleRes = Res.string.error, messageRes = Res.string.wifi_qr_code_error, onDismiss = onDismiss)
|
||||
|
||||
@Composable
|
||||
fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit) {
|
||||
|
|
@ -105,23 +103,16 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBac
|
|||
|
||||
var showNfcDisabledDialog: Boolean by rememberSaveable { mutableStateOf(false) }
|
||||
if (showNfcDisabledDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showNfcDisabledDialog = false },
|
||||
title = { Text(stringResource(Res.string.scan_nfc)) },
|
||||
text = { Text(stringResource(Res.string.nfc_disabled)) },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
context.openNfcSettings()
|
||||
showNfcDisabledDialog = false
|
||||
},
|
||||
) {
|
||||
Text(stringResource(Res.string.open_settings))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showNfcDisabledDialog = false }) { Text(stringResource(Res.string.cancel)) }
|
||||
MeshtasticDialog(
|
||||
onDismiss = { showNfcDisabledDialog = false },
|
||||
title = stringResource(Res.string.scan_nfc),
|
||||
message = stringResource(Res.string.nfc_disabled),
|
||||
confirmText = stringResource(Res.string.open_settings),
|
||||
onConfirm = {
|
||||
context.openNfcSettings()
|
||||
showNfcDisabledDialog = false
|
||||
},
|
||||
dismissText = stringResource(Res.string.cancel),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,14 +18,9 @@ package org.meshtastic.feature.settings.radio.component
|
|||
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -42,6 +37,7 @@ import org.meshtastic.core.strings.Res
|
|||
import org.meshtastic.core.strings.close
|
||||
import org.meshtastic.core.strings.delivery_confirmed
|
||||
import org.meshtastic.core.strings.error
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.feature.settings.radio.ResponseState
|
||||
|
||||
private const val AUTO_DISMISS_DELAY_MS = 1500L
|
||||
|
|
@ -55,10 +51,11 @@ fun <T> PacketResponseStateDialog(state: ResponseState<T>, onDismiss: () -> Unit
|
|||
onDismiss()
|
||||
}
|
||||
}
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
title = {
|
||||
|
||||
MeshtasticDialog(
|
||||
onDismiss = onDismiss,
|
||||
title = "", // Title is handled in the text block for more control
|
||||
text = {
|
||||
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
if (state is ResponseState.Loading) {
|
||||
val progress by
|
||||
|
|
@ -86,24 +83,15 @@ fun <T> PacketResponseStateDialog(state: ResponseState<T>, onDismiss: () -> Unit
|
|||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(start = 24.dp, end = 24.dp, bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
if (state is ResponseState.Success || state is ResponseState.Error) {
|
||||
backDispatcher?.onBackPressed()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
) {
|
||||
Text(stringResource(Res.string.close))
|
||||
}
|
||||
dismissable = false,
|
||||
onConfirm = {
|
||||
onDismiss()
|
||||
if (state is ResponseState.Success || state is ResponseState.Error) {
|
||||
backDispatcher?.onBackPressed()
|
||||
}
|
||||
},
|
||||
confirmText = stringResource(Res.string.close),
|
||||
dismissText = null, // Hide dismiss button, only show "Close" confirm button
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,12 +24,9 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.twotone.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -49,7 +46,6 @@ import org.meshtastic.core.strings.Res
|
|||
import org.meshtastic.core.strings.admin_key
|
||||
import org.meshtastic.core.strings.admin_keys
|
||||
import org.meshtastic.core.strings.administration
|
||||
import org.meshtastic.core.strings.cancel
|
||||
import org.meshtastic.core.strings.config_security_admin_key
|
||||
import org.meshtastic.core.strings.config_security_debug_log_api_enabled
|
||||
import org.meshtastic.core.strings.config_security_is_managed
|
||||
|
|
@ -63,7 +59,6 @@ import org.meshtastic.core.strings.export_keys_confirmation
|
|||
import org.meshtastic.core.strings.legacy_admin_channel
|
||||
import org.meshtastic.core.strings.logs
|
||||
import org.meshtastic.core.strings.managed_mode
|
||||
import org.meshtastic.core.strings.okay
|
||||
import org.meshtastic.core.strings.private_key
|
||||
import org.meshtastic.core.strings.public_key
|
||||
import org.meshtastic.core.strings.regenerate_keys_confirmation
|
||||
|
|
@ -73,6 +68,7 @@ import org.meshtastic.core.strings.serial_console
|
|||
import org.meshtastic.core.ui.component.CopyIconButton
|
||||
import org.meshtastic.core.ui.component.EditBase64Preference
|
||||
import org.meshtastic.core.ui.component.EditListPreference
|
||||
import org.meshtastic.core.ui.component.MeshtasticResourceDialog
|
||||
import org.meshtastic.core.ui.component.SwitchPreference
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.feature.settings.radio.RadioConfigViewModel
|
||||
|
|
@ -116,28 +112,22 @@ fun SecurityConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBa
|
|||
)
|
||||
var showEditSecurityConfigDialog by rememberSaveable { mutableStateOf(false) }
|
||||
if (showEditSecurityConfigDialog) {
|
||||
AlertDialog(
|
||||
title = { Text(text = stringResource(Res.string.export_keys)) },
|
||||
text = { Text(text = stringResource(Res.string.export_keys_confirmation)) },
|
||||
onDismissRequest = { showEditSecurityConfigDialog = false },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
showEditSecurityConfigDialog = false
|
||||
val intent =
|
||||
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/*"
|
||||
putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"${node?.user?.short_name}_keys_${System.currentTimeMillis()}.json",
|
||||
)
|
||||
}
|
||||
exportConfigLauncher.launch(intent)
|
||||
},
|
||||
) {
|
||||
Text(stringResource(Res.string.okay))
|
||||
}
|
||||
MeshtasticResourceDialog(
|
||||
titleRes = Res.string.export_keys,
|
||||
messageRes = Res.string.export_keys_confirmation,
|
||||
onDismiss = { showEditSecurityConfigDialog = false },
|
||||
onConfirm = {
|
||||
showEditSecurityConfigDialog = false
|
||||
val intent =
|
||||
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/*"
|
||||
putExtra(
|
||||
Intent.EXTRA_TITLE,
|
||||
"${node?.user?.short_name}_keys_${System.currentTimeMillis()}.json",
|
||||
)
|
||||
}
|
||||
exportConfigLauncher.launch(intent)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -268,30 +258,22 @@ fun PrivateKeyRegenerateDialog(
|
|||
onDismiss: () -> Unit = {},
|
||||
) {
|
||||
if (showKeyGenerationDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(text = stringResource(Res.string.regenerate_private_key)) },
|
||||
text = { Text(text = stringResource(Res.string.regenerate_keys_confirmation)) },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
// Generate a random "f" value
|
||||
val f = ByteArray(32).apply { SecureRandom().nextBytes(this) }
|
||||
// Adjust the value to make it valid as an "s" value for eval().
|
||||
// According to the specification we need to mask off the 3
|
||||
// right-most bits of f[0], mask off the left-most bit of f[31],
|
||||
// and set the second to left-most bit of f[31].
|
||||
f[0] = (f[0].toInt() and 0xF8).toByte()
|
||||
f[31] = ((f[31].toInt() and 0x7F) or 0x40).toByte()
|
||||
val securityInput =
|
||||
Config.SecurityConfig(private_key = f.toByteString(), public_key = ByteString.EMPTY)
|
||||
onConfirm(securityInput)
|
||||
},
|
||||
) {
|
||||
Text(stringResource(Res.string.okay))
|
||||
}
|
||||
MeshtasticResourceDialog(
|
||||
onDismiss = onDismiss,
|
||||
titleRes = Res.string.regenerate_private_key,
|
||||
messageRes = Res.string.regenerate_keys_confirmation,
|
||||
onConfirm = {
|
||||
// Generate a random "f" value
|
||||
val f = ByteArray(32).apply { SecureRandom().nextBytes(this) }
|
||||
// Adjust the value to make it valid as an "s" value for eval().
|
||||
// According to the specification we need to mask off the 3
|
||||
// right-most bits of f[0], mask off the left-most bit of f[31],
|
||||
// and set the second to left-most bit of f[31].
|
||||
f[0] = (f[0].toInt() and 0xF8).toByte()
|
||||
f[31] = ((f[31].toInt() and 0x7F) or 0x40).toByte()
|
||||
val securityInput = Config.SecurityConfig(private_key = f.toByteString(), public_key = ByteString.EMPTY)
|
||||
onConfirm(securityInput)
|
||||
},
|
||||
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(Res.string.cancel)) } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,8 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
|
@ -43,6 +39,7 @@ import org.meshtastic.core.strings.cancel
|
|||
import org.meshtastic.core.strings.send
|
||||
import org.meshtastic.core.strings.shutdown_node_name
|
||||
import org.meshtastic.core.strings.shutdown_warning
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.proto.User
|
||||
|
||||
|
|
@ -57,22 +54,17 @@ fun ShutdownConfirmationDialog(
|
|||
) {
|
||||
val nodeLongName = node?.user?.long_name ?: "Unknown Node"
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
icon = { icon?.let { Icon(imageVector = it, contentDescription = null) } },
|
||||
title = { Text(text = title) },
|
||||
MeshtasticDialog(
|
||||
onDismiss = onDismiss,
|
||||
icon = icon,
|
||||
title = title,
|
||||
text = { ShutdownDialogContent(nodeLongName = nodeLongName, isShutdown = isShutdown) },
|
||||
dismissButton = { TextButton(onClick = { onDismiss() }) { Text(stringResource(Res.string.cancel)) } },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
onConfirm()
|
||||
},
|
||||
) {
|
||||
Text(stringResource(Res.string.send))
|
||||
}
|
||||
confirmText = stringResource(Res.string.send),
|
||||
onConfirm = {
|
||||
onDismiss()
|
||||
onConfirm()
|
||||
},
|
||||
dismissText = stringResource(Res.string.cancel),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -14,16 +14,10 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.meshtastic.feature.settings.radio.component
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -31,6 +25,7 @@ import org.jetbrains.compose.resources.stringResource
|
|||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.cancel
|
||||
import org.meshtastic.core.strings.send
|
||||
import org.meshtastic.core.ui.component.MeshtasticDialog
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
|
|
@ -41,22 +36,17 @@ fun WarningDialog(
|
|||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
icon = { icon?.let { Icon(imageVector = it, contentDescription = null) } },
|
||||
title = { Text(text = title) },
|
||||
MeshtasticDialog(
|
||||
onDismiss = onDismiss,
|
||||
icon = icon,
|
||||
title = title,
|
||||
text = text,
|
||||
dismissButton = { TextButton(onClick = { onDismiss() }) { Text(stringResource(Res.string.cancel)) } },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
onConfirm()
|
||||
},
|
||||
) {
|
||||
Text(stringResource(Res.string.send))
|
||||
}
|
||||
confirmText = stringResource(Res.string.send),
|
||||
onConfirm = {
|
||||
onDismiss()
|
||||
onConfirm()
|
||||
},
|
||||
dismissText = stringResource(Res.string.cancel),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue