kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
Refactor: Settings to Connections, ui updates (#1984)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>pull/1975/head
rodzic
ad1897c564
commit
b861d07aba
|
@ -32,13 +32,13 @@ import androidx.navigation.toRoute
|
||||||
import com.geeksville.mesh.R
|
import com.geeksville.mesh.R
|
||||||
import com.geeksville.mesh.model.UIViewModel
|
import com.geeksville.mesh.model.UIViewModel
|
||||||
import com.geeksville.mesh.ui.TopLevelDestination.Companion.isTopLevel
|
import com.geeksville.mesh.ui.TopLevelDestination.Companion.isTopLevel
|
||||||
|
import com.geeksville.mesh.ui.connections.ConnectionsScreen
|
||||||
import com.geeksville.mesh.ui.contact.ContactsScreen
|
import com.geeksville.mesh.ui.contact.ContactsScreen
|
||||||
import com.geeksville.mesh.ui.debug.DebugScreen
|
import com.geeksville.mesh.ui.debug.DebugScreen
|
||||||
import com.geeksville.mesh.ui.map.MapView
|
import com.geeksville.mesh.ui.map.MapView
|
||||||
import com.geeksville.mesh.ui.message.MessageScreen
|
import com.geeksville.mesh.ui.message.MessageScreen
|
||||||
import com.geeksville.mesh.ui.message.QuickChatScreen
|
import com.geeksville.mesh.ui.message.QuickChatScreen
|
||||||
import com.geeksville.mesh.ui.node.NodeScreen
|
import com.geeksville.mesh.ui.node.NodeScreen
|
||||||
import com.geeksville.mesh.ui.settings.SettingsScreen
|
|
||||||
import com.geeksville.mesh.ui.sharing.ChannelScreen
|
import com.geeksville.mesh.ui.sharing.ChannelScreen
|
||||||
import com.geeksville.mesh.ui.sharing.ShareScreen
|
import com.geeksville.mesh.ui.sharing.ShareScreen
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
@ -76,7 +76,7 @@ sealed interface Route {
|
||||||
data object Channels : Route
|
data object Channels : Route
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Settings : Route
|
data object Connections : Route
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object DebugPanel : Route
|
data object DebugPanel : Route
|
||||||
|
@ -219,7 +219,7 @@ fun NavGraph(
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = if (uIViewModel.bondedAddress.isNullOrBlank()) {
|
startDestination = if (uIViewModel.bondedAddress.isNullOrBlank()) {
|
||||||
Route.Settings
|
Route.Connections
|
||||||
} else {
|
} else {
|
||||||
Route.Contacts
|
Route.Contacts
|
||||||
},
|
},
|
||||||
|
@ -244,19 +244,19 @@ fun NavGraph(
|
||||||
composable<Route.Channels> {
|
composable<Route.Channels> {
|
||||||
ChannelScreen(uIViewModel)
|
ChannelScreen(uIViewModel)
|
||||||
}
|
}
|
||||||
composable<Route.Settings>(
|
composable<Route.Connections>(
|
||||||
deepLinks = listOf(
|
deepLinks = listOf(
|
||||||
navDeepLink {
|
navDeepLink {
|
||||||
uriPattern = "$DEEP_LINK_BASE_URI/settings"
|
uriPattern = "$DEEP_LINK_BASE_URI/connections"
|
||||||
action = "android.intent.action.VIEW"
|
action = "android.intent.action.VIEW"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
) { backStackEntry ->
|
) { backStackEntry ->
|
||||||
SettingsScreen(
|
ConnectionsScreen(
|
||||||
uIViewModel,
|
uIViewModel,
|
||||||
onNavigateToRadioConfig = {
|
onNavigateToRadioConfig = {
|
||||||
navController.navigate(Route.RadioConfig()) {
|
navController.navigate(Route.RadioConfig()) {
|
||||||
popUpTo(Route.Settings) {
|
popUpTo(Route.Connections) {
|
||||||
inclusive = false
|
inclusive = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,7 @@
|
||||||
|
|
||||||
package com.geeksville.mesh.ui
|
package com.geeksville.mesh.ui
|
||||||
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.core.tween
|
|
||||||
import androidx.compose.animation.slideInVertically
|
|
||||||
import androidx.compose.animation.slideOutVertically
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||||
|
@ -36,7 +31,6 @@ import androidx.compose.material.icons.twotone.CloudUpload
|
||||||
import androidx.compose.material.icons.twotone.Contactless
|
import androidx.compose.material.icons.twotone.Contactless
|
||||||
import androidx.compose.material.icons.twotone.Map
|
import androidx.compose.material.icons.twotone.Map
|
||||||
import androidx.compose.material.icons.twotone.People
|
import androidx.compose.material.icons.twotone.People
|
||||||
import androidx.compose.material.icons.twotone.Settings
|
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
@ -45,10 +39,14 @@ import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.NavigationBar
|
||||||
import androidx.compose.material3.NavigationBarItem
|
import androidx.compose.material3.NavigationBarItem
|
||||||
|
import androidx.compose.material3.PlainTooltip
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TooltipBox
|
||||||
|
import androidx.compose.material3.TooltipDefaults
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.rememberTooltipState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
@ -56,8 +54,8 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
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.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
@ -82,12 +80,12 @@ import com.geeksville.mesh.ui.common.components.ScannedQrCodeDialog
|
||||||
import com.geeksville.mesh.ui.common.components.SimpleAlertDialog
|
import com.geeksville.mesh.ui.common.components.SimpleAlertDialog
|
||||||
import com.geeksville.mesh.ui.debug.DebugMenuActions
|
import com.geeksville.mesh.ui.debug.DebugMenuActions
|
||||||
|
|
||||||
enum class TopLevelDestination(val label: String, val icon: ImageVector, val route: Route) {
|
enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector, val route: Route) {
|
||||||
Contacts("Contacts", Icons.AutoMirrored.TwoTone.Chat, Route.Contacts),
|
Contacts(R.string.contacts, Icons.AutoMirrored.TwoTone.Chat, Route.Contacts),
|
||||||
Nodes("Nodes", Icons.TwoTone.People, Route.Nodes),
|
Nodes(R.string.nodes, Icons.TwoTone.People, Route.Nodes),
|
||||||
Map("Map", Icons.TwoTone.Map, Route.Map),
|
Map(R.string.map, Icons.TwoTone.Map, Route.Map),
|
||||||
Channels("Channels", Icons.TwoTone.Contactless, Route.Channels),
|
Channels(R.string.channels, Icons.TwoTone.Contactless, Route.Channels),
|
||||||
Settings("Settings", Icons.TwoTone.Settings, Route.Settings),
|
Connections(R.string.connections, Icons.TwoTone.CloudOff, Route.Connections),
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -109,7 +107,6 @@ fun MainScreen(
|
||||||
val connectionState by viewModel.connectionState.collectAsStateWithLifecycle()
|
val connectionState by viewModel.connectionState.collectAsStateWithLifecycle()
|
||||||
val localConfig by viewModel.localConfig.collectAsStateWithLifecycle()
|
val localConfig by viewModel.localConfig.collectAsStateWithLifecycle()
|
||||||
val requestChannelSet by viewModel.requestChannelSet.collectAsStateWithLifecycle()
|
val requestChannelSet by viewModel.requestChannelSet.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
if (connectionState.isConnected()) {
|
if (connectionState.isConnected()) {
|
||||||
requestChannelSet?.let { newChannelSet ->
|
requestChannelSet?.let { newChannelSet ->
|
||||||
ScannedQrCodeDialog(viewModel, newChannelSet)
|
ScannedQrCodeDialog(viewModel, newChannelSet)
|
||||||
|
@ -154,7 +151,6 @@ fun MainScreen(
|
||||||
MainAppBar(
|
MainAppBar(
|
||||||
title = title,
|
title = title,
|
||||||
isManaged = localConfig.security.isManaged,
|
isManaged = localConfig.security.isManaged,
|
||||||
connectionState = connectionState,
|
|
||||||
navController = navController,
|
navController = navController,
|
||||||
) { action ->
|
) { action ->
|
||||||
when (action) {
|
when (action) {
|
||||||
|
@ -167,6 +163,7 @@ fun MainScreen(
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
BottomNavigation(
|
BottomNavigation(
|
||||||
|
connectionState = connectionState,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -197,7 +194,6 @@ enum class MainMenuAction(@StringRes val stringRes: Int) {
|
||||||
private fun MainAppBar(
|
private fun MainAppBar(
|
||||||
title: String,
|
title: String,
|
||||||
isManaged: Boolean,
|
isManaged: Boolean,
|
||||||
connectionState: MeshService.ConnectionState,
|
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onAction: (MainMenuAction) -> Unit
|
onAction: (MainMenuAction) -> Unit
|
||||||
|
@ -267,7 +263,7 @@ private fun MainAppBar(
|
||||||
actions = {
|
actions = {
|
||||||
when {
|
when {
|
||||||
currentDestination == null || isTopLevelRoute ->
|
currentDestination == null || isTopLevelRoute ->
|
||||||
MainMenuActions(isManaged, connectionState, onAction)
|
MainMenuActions(isManaged, onAction)
|
||||||
|
|
||||||
currentDestination.hasRoute<Route.DebugPanel>() ->
|
currentDestination.hasRoute<Route.DebugPanel>() ->
|
||||||
DebugMenuActions()
|
DebugMenuActions()
|
||||||
|
@ -278,30 +274,13 @@ private fun MainAppBar(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun MainMenuActions(
|
private fun MainMenuActions(
|
||||||
isManaged: Boolean,
|
isManaged: Boolean,
|
||||||
connectionState: MeshService.ConnectionState,
|
|
||||||
onAction: (MainMenuAction) -> Unit
|
onAction: (MainMenuAction) -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
val (image, tooltip) = when (connectionState) {
|
|
||||||
MeshService.ConnectionState.CONNECTED -> Icons.TwoTone.CloudDone to R.string.connected
|
|
||||||
MeshService.ConnectionState.DEVICE_SLEEP -> Icons.TwoTone.CloudUpload to R.string.device_sleeping
|
|
||||||
MeshService.ConnectionState.DISCONNECTED -> Icons.TwoTone.CloudOff to R.string.disconnected
|
|
||||||
}
|
|
||||||
|
|
||||||
var showMenu by remember { mutableStateOf(false) }
|
var showMenu by remember { mutableStateOf(false) }
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
Toast.makeText(context, tooltip, Toast.LENGTH_SHORT).show()
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = image,
|
|
||||||
contentDescription = stringResource(id = tooltip),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
IconButton(onClick = { showMenu = true }) {
|
IconButton(onClick = { showMenu = true }) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.MoreVert,
|
imageVector = Icons.Default.MoreVert,
|
||||||
|
@ -330,39 +309,43 @@ private fun MainMenuActions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun BottomNavigation(
|
private fun BottomNavigation(
|
||||||
|
connectionState: MeshService.ConnectionState,
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
) {
|
) {
|
||||||
val currentDestination = navController.currentBackStackEntryAsState().value?.destination
|
val currentDestination = navController.currentBackStackEntryAsState().value?.destination
|
||||||
val topLevelDestination = TopLevelDestination.fromNavDestination(currentDestination)
|
val topLevelDestination = TopLevelDestination.fromNavDestination(currentDestination)
|
||||||
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = topLevelDestination != null,
|
|
||||||
enter = slideInVertically(
|
|
||||||
initialOffsetY = { it / 2 },
|
|
||||||
animationSpec = tween(durationMillis = 50),
|
|
||||||
),
|
|
||||||
exit = slideOutVertically(
|
|
||||||
targetOffsetY = { it / 2 },
|
|
||||||
animationSpec = tween(durationMillis = 50),
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
NavigationBar {
|
NavigationBar {
|
||||||
TopLevelDestination.entries.forEach {
|
TopLevelDestination.entries.forEach { destination ->
|
||||||
val isSelected = it == topLevelDestination
|
val isSelected = destination == topLevelDestination
|
||||||
|
val isConnectionsRoute = destination == TopLevelDestination.Connections
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
icon = {
|
icon = {
|
||||||
Icon(
|
TooltipBox(
|
||||||
imageVector = it.icon,
|
positionProvider = TooltipDefaults.rememberTooltipPositionProvider(),
|
||||||
contentDescription = it.name,
|
tooltip = {
|
||||||
)
|
PlainTooltip {
|
||||||
|
Text(
|
||||||
|
if (isConnectionsRoute) {
|
||||||
|
connectionState.getTooltipString()
|
||||||
|
} else {
|
||||||
|
stringResource(id = destination.label)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state = rememberTooltipState()
|
||||||
|
) {
|
||||||
|
TopLevelNavIcon(destination, connectionState)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// label = { Text(it.label) },
|
|
||||||
selected = isSelected,
|
selected = isSelected,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (!isSelected) {
|
if (!isSelected) {
|
||||||
navController.navigate(it.route) {
|
navController.navigate(destination.route) {
|
||||||
// Pop up to the start destination of the graph to
|
// Pop up to the start destination of the graph to
|
||||||
// avoid building up a large stack of destinations
|
// avoid building up a large stack of destinations
|
||||||
// on the back stack as users select items
|
// on the back stack as users select items
|
||||||
|
@ -380,5 +363,49 @@ private fun BottomNavigation(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MeshService.ConnectionState.getConnectionColor(): Color {
|
||||||
|
return when (this) {
|
||||||
|
MeshService.ConnectionState.CONNECTED -> Color(color = 0xFF30C047)
|
||||||
|
MeshService.ConnectionState.DEVICE_SLEEP -> MaterialTheme.colorScheme.tertiary
|
||||||
|
MeshService.ConnectionState.DISCONNECTED -> MaterialTheme.colorScheme.error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MeshService.ConnectionState.getConnectionIcon(): ImageVector {
|
||||||
|
return when (this) {
|
||||||
|
MeshService.ConnectionState.CONNECTED -> Icons.TwoTone.CloudDone
|
||||||
|
MeshService.ConnectionState.DEVICE_SLEEP -> Icons.TwoTone.CloudUpload
|
||||||
|
MeshService.ConnectionState.DISCONNECTED -> Icons.TwoTone.CloudOff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MeshService.ConnectionState.getTooltipString(): String {
|
||||||
|
return when (this) {
|
||||||
|
MeshService.ConnectionState.CONNECTED -> stringResource(R.string.connected)
|
||||||
|
MeshService.ConnectionState.DEVICE_SLEEP -> stringResource(R.string.device_sleeping)
|
||||||
|
MeshService.ConnectionState.DISCONNECTED -> stringResource(R.string.disconnected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TopLevelNavIcon(
|
||||||
|
dest: TopLevelDestination,
|
||||||
|
connectionState: MeshService.ConnectionState
|
||||||
|
) {
|
||||||
|
when (dest) {
|
||||||
|
TopLevelDestination.Connections -> Icon(
|
||||||
|
imageVector = connectionState.getConnectionIcon(),
|
||||||
|
contentDescription = stringResource(id = dest.label),
|
||||||
|
tint = connectionState.getConnectionColor(),
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> Icon(
|
||||||
|
imageVector = dest.icon,
|
||||||
|
contentDescription = stringResource(id = dest.label),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ package com.geeksville.mesh.ui.common.theme
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.MaterialExpressiveTheme
|
||||||
import androidx.compose.material3.darkColorScheme
|
import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.material3.dynamicDarkColorScheme
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
import androidx.compose.material3.dynamicLightColorScheme
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
|
@ -271,10 +272,10 @@ val unspecified_scheme = ColorFamily(
|
||||||
Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified
|
Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AppTheme(
|
fun AppTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
// Dynamic color is available on Android 12+
|
|
||||||
dynamicColor: Boolean = true,
|
dynamicColor: Boolean = true,
|
||||||
content: @Composable() () -> Unit
|
content: @Composable() () -> Unit
|
||||||
) {
|
) {
|
||||||
|
@ -288,7 +289,7 @@ fun AppTheme(
|
||||||
else -> lightScheme
|
else -> lightScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialTheme(
|
MaterialExpressiveTheme(
|
||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
typography = AppTypography,
|
typography = AppTypography,
|
||||||
content = content
|
content = content
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.geeksville.mesh.ui.settings
|
package com.geeksville.mesh.ui.connections
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
@ -108,9 +108,9 @@ fun String?.isIPAddress(): Boolean {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("CyclomaticComplexMethod", "LongMethod")
|
@Suppress("CyclomaticComplexMethod", "LongMethod", "MagicNumber")
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
fun ConnectionsScreen(
|
||||||
uiViewModel: UIViewModel = hiltViewModel(),
|
uiViewModel: UIViewModel = hiltViewModel(),
|
||||||
scanModel: BTScanModel = hiltViewModel(),
|
scanModel: BTScanModel = hiltViewModel(),
|
||||||
bluetoothViewModel: BluetoothViewModel = hiltViewModel(),
|
bluetoothViewModel: BluetoothViewModel = hiltViewModel(),
|
||||||
|
@ -389,7 +389,7 @@ fun SettingsScreen(
|
||||||
label = { Text(stringResource(R.string.ip_address)) },
|
label = { Text(stringResource(R.string.ip_address)) },
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(0.7f)
|
||||||
.padding(start = 16.dp)
|
.padding(start = 16.dp)
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
|
@ -1,3 +1,20 @@
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2025 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
|
||||||
|
~ the Free Software Foundation, either version 3 of the License, or
|
||||||
|
~ (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ This program is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
~ GNU General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License
|
||||||
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
// Language tags native names (not available via .getDisplayLanguage)
|
// Language tags native names (not available via .getDisplayLanguage)
|
||||||
<string name="fr_HT" translatable="false">Kreyòl ayisyen</string>
|
<string name="fr_HT" translatable="false">Kreyòl ayisyen</string>
|
||||||
|
@ -638,4 +655,8 @@
|
||||||
<string name="disk_free">Disk Free</string>
|
<string name="disk_free">Disk Free</string>
|
||||||
<string name="load">Load</string>
|
<string name="load">Load</string>
|
||||||
<string name="user_string">User String</string>
|
<string name="user_string">User String</string>
|
||||||
|
<string name="connections">Connections</string>
|
||||||
|
<string name="map">Map</string>
|
||||||
|
<string name="contacts">Contacts</string>
|
||||||
|
<string name="nodes">Nodes</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Ładowanie…
Reference in New Issue