diff --git a/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt
index 03f54033..19a6fcd6 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt
@@ -9,14 +9,24 @@ import android.view.ViewGroup
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes
+import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.AlertDialog
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Card
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
@@ -38,6 +48,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -59,8 +70,6 @@ import com.geeksville.mesh.NodeInfo
import com.geeksville.mesh.Portnums
import com.geeksville.mesh.R
import com.geeksville.mesh.android.Logging
-import com.geeksville.mesh.channel
-import com.geeksville.mesh.channelSettings
import com.geeksville.mesh.config
import com.geeksville.mesh.deviceProfile
import com.geeksville.mesh.model.UIViewModel
@@ -337,6 +346,27 @@ fun RadioConfigNavHost(node: NodeInfo, viewModel: UIViewModel = viewModel()) {
packetResponseState = PacketResponseState.Empty
showEditDeviceProfileDialog = true
}
+
+ "REBOOT" -> {
+ packetResponseState = PacketResponseState.Empty
+ viewModel.requestReboot(destNum)
+ }
+
+ "SHUTDOWN" -> {
+ packetResponseState = PacketResponseState.Empty
+ viewModel.requestShutdown(destNum)
+ }
+
+ "FACTORY_RESET" -> {
+ packetResponseState = PacketResponseState.Empty
+ viewModel.requestFactoryReset(destNum)
+ }
+
+ "NODEDB_RESET" -> {
+ packetResponseState = PacketResponseState.Empty
+ viewModel.requestNodedbReset(destNum)
+ }
+
is ConfigType -> {
viewModel.getConfig(destNum, configType.number)
}
@@ -363,20 +393,7 @@ fun RadioConfigNavHost(node: NodeInfo, viewModel: UIViewModel = viewModel()) {
focusManager = focusManager,
onSaveClicked = { channelListInput ->
focusManager.clearFocus()
- (0 until channelList.size.coerceAtLeast(channelListInput.size)).map { i ->
- channel {
- role = when (i) {
- 0 -> ChannelProtos.Channel.Role.PRIMARY
- in 1 until channelListInput.size -> ChannelProtos.Channel.Role.SECONDARY
- else -> ChannelProtos.Channel.Role.DISABLED
- }
- index = i
- settings = channelListInput.getOrNull(i) ?: channelSettings { }
- }
- }.forEach { newChannel ->
- if (newChannel.settings != channelList.getOrNull(newChannel.index))
- viewModel.setRemoteChannel(destNum, newChannel)
- }
+ viewModel.updateChannels(destNum, channelList, channelListInput)
channelList.clear()
channelList.addAll(channelListInput)
}
@@ -633,6 +650,9 @@ fun NavCard(
enabled: Boolean,
onClick: () -> Unit
) {
+ val color = if (enabled) MaterialTheme.colors.onSurface
+ else MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
+
Card(
modifier = Modifier
.fillMaxWidth()
@@ -642,17 +662,18 @@ fun NavCard(
) {
Row(
verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.padding(vertical = 16.dp, horizontal = 16.dp)
+ modifier = Modifier.padding(vertical = 12.dp, horizontal = 12.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.body1,
- color = if (!enabled) MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled) else Color.Unspecified,
+ color = color,
modifier = Modifier.weight(1f)
)
Icon(
Icons.TwoTone.KeyboardArrowRight, "trailingIcon",
modifier = Modifier.wrapContentSize(),
+ tint = color,
)
}
}
@@ -663,6 +684,67 @@ fun NavCard(@StringRes title: Int, enabled: Boolean, onClick: () -> Unit) {
NavCard(title = stringResource(title), enabled = enabled, onClick = onClick)
}
+@Composable
+fun NavButton(@StringRes title: Int, enabled: Boolean, onClick: () -> Unit) {
+ var showDialog by remember { mutableStateOf(false) }
+ if (showDialog) AlertDialog(
+ onDismissRequest = { },
+ title = {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ Icon(
+ painterResource(R.drawable.ic_twotone_warning_24),
+ "warning",
+ modifier = Modifier.padding(end = 8.dp)
+ )
+ Text(
+ text = "${stringResource(title)}?\n")
+ Icon(
+ painterResource(R.drawable.ic_twotone_warning_24),
+ "warning",
+ modifier = Modifier.padding(start = 8.dp)
+ )
+ }
+ },
+ buttons = {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ Button(
+ modifier = Modifier.weight(1f),
+ onClick = { showDialog = false }
+ ) { Text(stringResource(R.string.cancel)) }
+ Button(
+ modifier = Modifier.weight(1f),
+ onClick = {
+ showDialog = false
+ onClick()
+ },
+ ) { Text(stringResource(R.string.send)) }
+ }
+ }
+ )
+
+ Column {
+ val borderColor = if (isSystemInDarkTheme()) Color.DarkGray else Color.Unspecified
+ Spacer(modifier = Modifier.height(4.dp))
+ Button(
+ modifier = Modifier
+ .fillMaxWidth()
+ .border(BorderStroke(1.dp, borderColor), shape = MaterialTheme.shapes.medium)
+ .height(48.dp),
+ enabled = enabled,
+ onClick = { showDialog = true },
+ colors = ButtonDefaults.buttonColors(
+ disabledContentColor = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
+ )
+ ) { Text(text = stringResource(title)) }
+ }
+}
+
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun RadioSettingsScreen(
@@ -693,6 +775,11 @@ fun RadioSettingsScreen(
item { NavCard("Import configuration", enabled = enabled) { onRouteClick("IMPORT") } }
item { NavCard("Export configuration", enabled = enabled) { onRouteClick("EXPORT") } }
}
+
+ item { NavButton(R.string.reboot, enabled) { onRouteClick("REBOOT") } }
+ item { NavButton(R.string.shutdown, enabled) { onRouteClick("SHUTDOWN") } }
+ item { NavButton(R.string.factory_reset, enabled) { onRouteClick("FACTORY_RESET") } }
+ item { NavButton(R.string.nodedb_reset, enabled) { onRouteClick("NODEDB_RESET") } }
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt
index e10579dd..e382609d 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt
@@ -64,6 +64,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
popup.inflate(R.menu.menu_nodes)
popup.menu.setGroupVisible(R.id.group_remote, position > 0)
popup.menu.setGroupVisible(R.id.group_admin, showAdmin)
+ popup.menu.setGroupEnabled(R.id.group_admin, !model.isManaged)
popup.setOnMenuItemClickListener { item: MenuItem ->
when (item.itemId) {
R.id.direct_message -> {
@@ -101,56 +102,6 @@ class UsersFragment : ScreenFragment("Users"), Logging {
.addToBackStack(null)
.commit()
}
- R.id.reboot -> {
- MaterialAlertDialogBuilder(requireContext())
- .setTitle("${getString(R.string.reboot)}\n${user?.longName}?")
- .setIcon(R.drawable.ic_twotone_warning_24)
- .setNeutralButton(R.string.cancel) { _, _ ->
- }
- .setPositiveButton(getString(R.string.okay)) { _, _ ->
- debug("User clicked requestReboot")
- model.requestReboot(node.num)
- }
- .show()
- }
- R.id.shutdown -> {
- MaterialAlertDialogBuilder(requireContext())
- .setTitle("${getString(R.string.shutdown)}\n${user?.longName}?")
- .setIcon(R.drawable.ic_twotone_warning_24)
- .setNeutralButton(R.string.cancel) { _, _ ->
- }
- .setPositiveButton(getString(R.string.okay)) { _, _ ->
- debug("User clicked requestShutdown")
- model.requestShutdown(node.num)
- }
- .show()
- }
- R.id.factory_reset -> {
- MaterialAlertDialogBuilder(requireContext())
- .setTitle("${getString(R.string.factory_reset)}\n${user?.longName}?")
- .setIcon(R.drawable.ic_twotone_warning_24)
- .setMessage(R.string.factory_reset_description)
- .setNeutralButton(R.string.cancel) { _, _ ->
- }
- .setPositiveButton(R.string.okay) { _, _ ->
- debug("User clicked requestFactoryReset")
- model.requestFactoryReset(node.num)
- }
- .show()
- }
- R.id.nodedb_reset -> {
- MaterialAlertDialogBuilder(requireContext())
- .setTitle("${getString(R.string.nodedb_reset)}\n${user?.longName}?")
- .setIcon(R.drawable.ic_twotone_warning_24)
- .setMessage(R.string.nodedb_reset_description)
- .setNeutralButton(R.string.cancel) { _, _ ->
- }
- .setPositiveButton(getString(R.string.okay)) { _, _ ->
- debug("User clicked requestNodedbReset")
- model.requestNodedbReset(node.num)
- }
- .show()
- }
}
true
}
diff --git a/app/src/main/res/menu/menu_nodes.xml b/app/src/main/res/menu/menu_nodes.xml
index 198655b3..c921e1c6 100644
--- a/app/src/main/res/menu/menu_nodes.xml
+++ b/app/src/main/res/menu/menu_nodes.xml
@@ -20,21 +20,5 @@
android:id="@+id/remote_admin"
android:title="@string/device_settings"
app:showAsAction="withText" />
-
-
-
-
\ No newline at end of file