kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
Move remaining 3-dot menu items to Settings (#2985)
rodzic
4ab588cdaa
commit
08ced48652
|
@ -28,7 +28,6 @@ import android.os.Bundle
|
|||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
|
@ -47,7 +46,6 @@ import com.geeksville.mesh.model.BluetoothViewModel
|
|||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.navigation.DEEP_LINK_BASE_URI
|
||||
import com.geeksville.mesh.ui.MainScreen
|
||||
import com.geeksville.mesh.ui.common.components.MainMenuAction
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.common.theme.MODE_DYNAMIC
|
||||
import com.geeksville.mesh.ui.intro.AppIntroductionScreen
|
||||
|
@ -117,11 +115,7 @@ class MainActivity :
|
|||
},
|
||||
)
|
||||
} else {
|
||||
MainScreen(
|
||||
uIViewModel = model,
|
||||
bluetoothViewModel = bluetoothViewModel,
|
||||
onAction = ::onMainMenuAction,
|
||||
)
|
||||
MainScreen(uIViewModel = model, bluetoothViewModel = bluetoothViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -204,30 +198,7 @@ class MainActivity :
|
|||
return resultPendingIntent!!
|
||||
}
|
||||
|
||||
private val createRangetestLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
it.data?.data?.let { file_uri -> model.saveRangetestCSV(file_uri) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSettingsPage() {
|
||||
createSettingsIntent().send()
|
||||
}
|
||||
|
||||
private fun onMainMenuAction(action: MainMenuAction) {
|
||||
when (action) {
|
||||
MainMenuAction.EXPORT_RANGETEST -> {
|
||||
val intent =
|
||||
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/csv"
|
||||
putExtra(Intent.EXTRA_TITLE, "rangetest.csv")
|
||||
}
|
||||
createRangetestLauncher.launch(intent)
|
||||
}
|
||||
|
||||
else -> warn("Unexpected action: $action")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -847,7 +847,7 @@ constructor(
|
|||
|
||||
/** Write the persisted packet data out to a CSV file in the specified location. */
|
||||
@Suppress("detekt:CyclomaticComplexMethod", "detekt:LongMethod")
|
||||
fun saveRangetestCSV(uri: Uri) {
|
||||
fun saveRangeTestCsv(uri: Uri) {
|
||||
viewModelScope.launch(Dispatchers.Main) {
|
||||
// Extract distances to this device from position messages and put (node,SNR,distance)
|
||||
// in
|
||||
|
|
|
@ -94,7 +94,6 @@ import com.geeksville.mesh.repository.radio.MeshActivity
|
|||
import com.geeksville.mesh.service.ConnectionState
|
||||
import com.geeksville.mesh.service.MeshService
|
||||
import com.geeksville.mesh.ui.common.components.MainAppBar
|
||||
import com.geeksville.mesh.ui.common.components.MainMenuAction
|
||||
import com.geeksville.mesh.ui.common.components.MultipleChoiceAlertDialog
|
||||
import com.geeksville.mesh.ui.common.components.ScannedQrCodeDialog
|
||||
import com.geeksville.mesh.ui.common.components.SimpleAlertDialog
|
||||
|
@ -146,7 +145,6 @@ fun MainScreen(
|
|||
uIViewModel: UIViewModel = hiltViewModel(),
|
||||
bluetoothViewModel: BluetoothViewModel = hiltViewModel(),
|
||||
scanModel: BTScanModel = hiltViewModel(),
|
||||
onAction: (MainMenuAction) -> Unit,
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
val connectionState by uIViewModel.connectionState.collectAsStateWithLifecycle()
|
||||
|
@ -349,12 +347,6 @@ fun MainScreen(
|
|||
viewModel = uIViewModel,
|
||||
navController = navController,
|
||||
onAction = { action ->
|
||||
if (action is MainMenuAction) {
|
||||
when (action) {
|
||||
MainMenuAction.QUICK_CHAT -> navController.navigate(ContactsRoutes.QuickChat)
|
||||
else -> onAction(action)
|
||||
}
|
||||
} else if (action is NodeMenuAction) {
|
||||
when (action) {
|
||||
is NodeMenuAction.MoreDetails -> {
|
||||
navController.navigate(
|
||||
|
@ -369,7 +361,6 @@ fun MainScreen(
|
|||
is NodeMenuAction.Share -> sharedContact = action.node
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
NavGraph(
|
||||
|
|
|
@ -17,27 +17,21 @@
|
|||
|
||||
package com.geeksville.mesh.ui.common.components
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
@ -45,6 +39,7 @@ import androidx.compose.ui.res.vectorResource
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavDestination.Companion.hasRoute
|
||||
|
@ -61,7 +56,7 @@ import com.geeksville.mesh.ui.TopLevelDestination.Companion.isTopLevel
|
|||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.debug.DebugMenuActions
|
||||
import com.geeksville.mesh.ui.node.components.NodeChip
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigMenuActions
|
||||
import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
@ -69,7 +64,7 @@ fun MainAppBar(
|
|||
modifier: Modifier = Modifier,
|
||||
viewModel: UIViewModel = hiltViewModel(),
|
||||
navController: NavHostController,
|
||||
onAction: (Any?) -> Unit,
|
||||
onAction: (NodeMenuAction) -> Unit,
|
||||
) {
|
||||
val backStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentDestination = backStackEntry?.destination
|
||||
|
@ -117,13 +112,7 @@ fun MainAppBar(
|
|||
actions = {
|
||||
currentDestination?.let {
|
||||
when {
|
||||
it.isTopLevel() -> MainMenuActions(onAction)
|
||||
|
||||
currentDestination.hasRoute<SettingsRoutes.DebugPanel>() -> DebugMenuActions()
|
||||
|
||||
currentDestination.hasRoute<SettingsRoutes.Settings>() ->
|
||||
RadioConfigMenuActions(viewModel = viewModel)
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +133,7 @@ private fun MainAppBar(
|
|||
canNavigateUp: Boolean,
|
||||
onNavigateUp: () -> Unit,
|
||||
actions: @Composable () -> Unit,
|
||||
onAction: (Any?) -> Unit,
|
||||
onAction: (NodeMenuAction) -> Unit,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
|
@ -195,43 +184,21 @@ private fun TopBarActions(
|
|||
isConnected: Boolean,
|
||||
showNodeChip: Boolean,
|
||||
actions: @Composable () -> Unit,
|
||||
onAction: (Any?) -> Unit,
|
||||
onAction: (NodeMenuAction) -> Unit,
|
||||
) {
|
||||
AnimatedVisibility(showNodeChip) {
|
||||
ourNode?.let { NodeChip(node = it, isThisNode = true, isConnected = isConnected, onAction = onAction) }
|
||||
}
|
||||
|
||||
actions()
|
||||
}
|
||||
|
||||
enum class MainMenuAction(@StringRes val stringRes: Int) {
|
||||
EXPORT_RANGETEST(R.string.save_rangetest),
|
||||
QUICK_CHAT(R.string.quick_chat),
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MainMenuActions(onAction: (MainMenuAction) -> Unit) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = { showMenu = true }) {
|
||||
Icon(imageVector = Icons.Default.MoreVert, contentDescription = stringResource(R.string.overflow_menu))
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false },
|
||||
modifier = Modifier.background(colorScheme.background.copy(alpha = 1f)),
|
||||
) {
|
||||
MainMenuAction.entries.forEach { action ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(id = action.stringRes)) },
|
||||
onClick = {
|
||||
onAction(action)
|
||||
showMenu = false
|
||||
},
|
||||
enabled = true,
|
||||
AnimatedVisibility(visible = showNodeChip, enter = fadeIn(), exit = fadeOut()) {
|
||||
ourNode?.let {
|
||||
NodeChip(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
node = it,
|
||||
isThisNode = true,
|
||||
isConnected = isConnected,
|
||||
onAction = onAction,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
actions()
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
|
@ -246,7 +213,7 @@ private fun MainAppBarPreview(@PreviewParameter(BooleanProvider::class) canNavig
|
|||
showNodeChip = true,
|
||||
canNavigateUp = canNavigateUp,
|
||||
onNavigateUp = {},
|
||||
actions = { MainMenuActions(onAction = {}) },
|
||||
actions = {},
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.net.InetAddresses
|
|||
import android.os.Build
|
||||
import android.util.Patterns
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
|
@ -77,7 +76,6 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.gpsDisabled
|
||||
|
@ -472,14 +470,12 @@ fun ConnectionsScreen(
|
|||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
|
||||
Row(
|
||||
Text(
|
||||
text = scanStatusText.orEmpty(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(text = BuildConfig.VERSION_NAME, fontSize = 10.sp, textAlign = TextAlign.Start)
|
||||
Text(text = scanStatusText.orEmpty(), fontSize = 10.sp, textAlign = TextAlign.End)
|
||||
}
|
||||
fontSize = 10.sp,
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,10 @@ package com.geeksville.mesh.ui.settings
|
|||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
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.padding
|
||||
|
@ -30,9 +32,13 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.filled.BugReport
|
||||
import androidx.compose.material.icons.rounded.FormatPaint
|
||||
import androidx.compose.material.icons.rounded.Language
|
||||
import androidx.compose.material.icons.rounded.Memory
|
||||
import androidx.compose.material.icons.rounded.Output
|
||||
import androidx.compose.material.icons.rounded.WavingHand
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
|
@ -42,8 +48,8 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
|
||||
import com.geeksville.mesh.DeviceUIProtos.Language
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.BuildUtils.debug
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
|
@ -52,12 +58,15 @@ import com.geeksville.mesh.navigation.getNavRouteFrom
|
|||
import com.geeksville.mesh.ui.common.components.TitledCard
|
||||
import com.geeksville.mesh.ui.common.theme.MODE_DYNAMIC
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItem
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItemDetail
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItemSwitch
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigItemList
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.components.EditDeviceProfileDialog
|
||||
import com.geeksville.mesh.ui.settings.radio.components.PacketResponseStateDialog
|
||||
import com.geeksville.mesh.util.LanguageUtils
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
@ -166,7 +175,7 @@ fun SettingsScreen(
|
|||
onNavigate = onNavigate,
|
||||
)
|
||||
|
||||
TitledCard(title = stringResource(R.string.phone_settings), modifier = Modifier.padding(top = 16.dp)) {
|
||||
TitledCard(title = stringResource(R.string.app_settings), modifier = Modifier.padding(top = 16.dp)) {
|
||||
if (state.analyticsAvailable) {
|
||||
SettingsItemSwitch(
|
||||
text = stringResource(R.string.analytics_okay),
|
||||
|
@ -214,6 +223,26 @@ fun SettingsScreen(
|
|||
)
|
||||
}
|
||||
|
||||
val exportRangeTestLauncher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == RESULT_OK) {
|
||||
it.data?.data?.let { uri -> uiViewModel.saveRangeTestCsv(uri) }
|
||||
}
|
||||
}
|
||||
SettingsItem(
|
||||
text = stringResource(R.string.save_rangetest),
|
||||
leadingIcon = Icons.Rounded.Output,
|
||||
trailingIcon = null,
|
||||
) {
|
||||
val intent =
|
||||
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/csv"
|
||||
putExtra(Intent.EXTRA_TITLE, "rangetest.csv")
|
||||
}
|
||||
exportRangeTestLauncher.launch(intent)
|
||||
}
|
||||
|
||||
SettingsItem(
|
||||
text = stringResource(R.string.intro_show),
|
||||
leadingIcon = Icons.Rounded.WavingHand,
|
||||
|
@ -221,6 +250,46 @@ fun SettingsScreen(
|
|||
) {
|
||||
uiViewModel.showAppIntro()
|
||||
}
|
||||
|
||||
AppVersionButton(excludedModulesUnlocked) { uiViewModel.unlockExcludedModules() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val UNLOCK_CLICK_COUNT = 5 // Number of clicks required to unlock excluded modules.
|
||||
private const val UNLOCKED_CLICK_COUNT = 3 // Number of clicks before we toast that modules are already unlocked.
|
||||
private const val UNLOCK_TIMEOUT_SECONDS = 1 // Timeout in seconds to reset the click counter.
|
||||
|
||||
/** A button to display the app version. Clicking it 5 times will unlock the excluded modules. */
|
||||
@Composable
|
||||
private fun AppVersionButton(excludedModulesUnlocked: Boolean, onUnlockExcludedModules: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
var clickCount by remember { mutableIntStateOf(0) }
|
||||
|
||||
LaunchedEffect(clickCount) {
|
||||
if (clickCount in 1..<UNLOCK_CLICK_COUNT) {
|
||||
delay(UNLOCK_TIMEOUT_SECONDS.seconds)
|
||||
clickCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
SettingsItemDetail(
|
||||
text = stringResource(R.string.app_version),
|
||||
icon = Icons.Rounded.Memory,
|
||||
trailingText = BuildConfig.VERSION_NAME,
|
||||
) {
|
||||
clickCount = clickCount.inc().coerceIn(0, UNLOCK_CLICK_COUNT)
|
||||
|
||||
when {
|
||||
clickCount == UNLOCKED_CLICK_COUNT && excludedModulesUnlocked -> {
|
||||
clickCount = 0
|
||||
Toast.makeText(context, context.getString(R.string.modules_already_unlocked), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
clickCount == UNLOCK_CLICK_COUNT -> {
|
||||
clickCount = 0
|
||||
onUnlockExcludedModules()
|
||||
Toast.makeText(context, context.getString(R.string.modules_unlocked), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package com.geeksville.mesh.ui.settings.radio
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
|
@ -25,24 +24,19 @@ import androidx.compose.material.icons.filled.Download
|
|||
import androidx.compose.material.icons.filled.Upload
|
||||
import androidx.compose.material.icons.rounded.BugReport
|
||||
import androidx.compose.material.icons.rounded.CleaningServices
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.navigation.AdminRoute
|
||||
import com.geeksville.mesh.navigation.ConfigRoute
|
||||
import com.geeksville.mesh.navigation.ModuleRoute
|
||||
|
@ -53,8 +47,6 @@ import com.geeksville.mesh.ui.common.theme.AppTheme
|
|||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItem
|
||||
import com.geeksville.mesh.ui.settings.radio.components.WarningDialog
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
@ -168,32 +160,6 @@ fun RadioConfigItemList(
|
|||
}
|
||||
}
|
||||
|
||||
private const val UNLOCK_CLICK_COUNT = 5 // Number of clicks required to unlock excluded modules.
|
||||
private const val UNLOCK_TIMEOUT_SECONDS = 3 // Timeout in seconds to reset the click counter.
|
||||
|
||||
@Composable
|
||||
fun RadioConfigMenuActions(modifier: Modifier = Modifier, viewModel: UIViewModel = hiltViewModel()) {
|
||||
val context = LocalContext.current
|
||||
var counter by remember { mutableIntStateOf(0) }
|
||||
LaunchedEffect(counter) {
|
||||
if (counter > 0 && counter < UNLOCK_CLICK_COUNT) {
|
||||
delay(UNLOCK_TIMEOUT_SECONDS.seconds)
|
||||
counter = 0
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
enabled = counter < UNLOCK_CLICK_COUNT,
|
||||
onClick = {
|
||||
counter++
|
||||
if (counter == UNLOCK_CLICK_COUNT) {
|
||||
viewModel.unlockExcludedModules()
|
||||
Toast.makeText(context, context.getString(R.string.modules_unlocked), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
) {}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun RadioSettingsScreenPreview() = AppTheme {
|
||||
|
|
|
@ -653,6 +653,7 @@
|
|||
<string name="export_keys">Export Keys</string>
|
||||
<string name="export_keys_confirmation">Exports public and private keys to a file. Please store somewhere securely.</string>
|
||||
<string name="modules_unlocked">Modules unlocked</string>
|
||||
<string name="modules_already_unlocked">Modules already unlocked</string>
|
||||
<string name="remote">Remote</string>
|
||||
<string name="node_count_template">(%1$d online / %2$d total)</string>
|
||||
<string name="react">React</string>
|
||||
|
@ -801,7 +802,8 @@
|
|||
<string name="url_template">URL Template</string>
|
||||
<string name="url_template_hint" translatable="false">https://a.tile.openstreetmap.org/{z}/{x}/{y}.png</string>
|
||||
<string name="track_point">track point</string>
|
||||
<string name="phone_settings">Phone Settings</string>
|
||||
<string name="app_settings">App</string>
|
||||
<string name="app_version">Version</string>
|
||||
<string name="channel_features">Channel Features</string>
|
||||
<string name="location_sharing">Location Sharing</string>
|
||||
<string name="periodic_position_broadcast">Periodic position broadcast</string>
|
||||
|
|
Ładowanie…
Reference in New Issue