mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
chore: KMP audit — commonize code, centralize utilities, eliminate dead abstractions (#5133)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
50ade01e55
commit
72b981f73b
132 changed files with 2186 additions and 916 deletions
|
|
@ -30,15 +30,16 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.eygraber.uri.toKmpUri
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.common.util.nowMillis
|
||||
import org.meshtastic.core.common.util.toDate
|
||||
import org.meshtastic.core.common.util.toInstant
|
||||
import org.meshtastic.core.common.util.toMeshtasticUri
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoute
|
||||
import org.meshtastic.core.navigation.WifiProvisionRoute
|
||||
|
|
@ -89,14 +90,14 @@ fun SettingsScreen(
|
|||
val state by viewModel.radioConfigState.collectAsStateWithLifecycle()
|
||||
|
||||
var deviceProfile by remember { mutableStateOf<DeviceProfile?>(null) }
|
||||
var showEditDeviceProfileDialog by remember { mutableStateOf(false) }
|
||||
var showEditDeviceProfileDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val importConfigLauncher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
showEditDeviceProfileDialog = true
|
||||
it.data?.data?.let { uri ->
|
||||
viewModel.importProfile(uri.toMeshtasticUri()) { profile -> deviceProfile = profile }
|
||||
viewModel.importProfile(uri.toKmpUri()) { profile -> deviceProfile = profile }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +105,7 @@ fun SettingsScreen(
|
|||
val exportConfigLauncher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
it.data?.data?.let { uri -> viewModel.exportProfile(uri.toMeshtasticUri(), deviceProfile!!) }
|
||||
it.data?.data?.let { uri -> viewModel.exportProfile(uri.toKmpUri(), deviceProfile!!) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,12 +144,12 @@ fun SettingsScreen(
|
|||
)
|
||||
}
|
||||
|
||||
var showLanguagePickerDialog by remember { mutableStateOf(false) }
|
||||
var showLanguagePickerDialog by rememberSaveable { mutableStateOf(false) }
|
||||
if (showLanguagePickerDialog) {
|
||||
LanguagePickerDialog { showLanguagePickerDialog = false }
|
||||
}
|
||||
|
||||
var showThemePickerDialog by remember { mutableStateOf(false) }
|
||||
var showThemePickerDialog by rememberSaveable { mutableStateOf(false) }
|
||||
if (showThemePickerDialog) {
|
||||
ThemePickerDialog(
|
||||
onClickTheme = { settingsViewModel.setTheme(it) },
|
||||
|
|
@ -249,7 +250,7 @@ fun SettingsScreen(
|
|||
cacheLimit = settingsViewModel.dbCacheLimit.collectAsStateWithLifecycle().value,
|
||||
onSetCacheLimit = { settingsViewModel.setDbCacheLimit(it) },
|
||||
nodeShortName = ourNode?.user?.short_name ?: "",
|
||||
onExportData = { settingsViewModel.saveDataCsv(it.toMeshtasticUri()) },
|
||||
onExportData = { settingsViewModel.saveDataCsv(it.toKmpUri()) },
|
||||
)
|
||||
|
||||
AppInfoSection(
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.eygraber.uri.toKmpUri
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.common.util.nowMillis
|
||||
import org.meshtastic.core.common.util.toMeshtasticUri
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.export_keys
|
||||
import org.meshtastic.core.resources.export_keys_confirmation
|
||||
|
|
@ -54,7 +54,7 @@ actual fun ExportSecurityConfigButton(
|
|||
val exportConfigLauncher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
it.data?.data?.let { uri -> viewModel.exportSecurityConfig(uri.toMeshtasticUri(), securityConfig) }
|
||||
it.data?.data?.let { uri -> viewModel.exportSecurityConfig(uri.toKmpUri(), securityConfig) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import okio.BufferedSink
|
|||
import org.koin.core.annotation.KoinViewModel
|
||||
import org.meshtastic.core.common.BuildConfigProvider
|
||||
import org.meshtastic.core.common.database.DatabaseManager
|
||||
import org.meshtastic.core.common.util.MeshtasticUri
|
||||
import org.meshtastic.core.common.util.CommonUri
|
||||
import org.meshtastic.core.domain.usecase.settings.ExportDataUseCase
|
||||
import org.meshtastic.core.domain.usecase.settings.IsOtaCapableUseCase
|
||||
import org.meshtastic.core.domain.usecase.settings.MeshLocationUseCase
|
||||
|
|
@ -187,7 +187,7 @@ class SettingsViewModel(
|
|||
* @param uri The destination URI for the CSV file.
|
||||
* @param filterPortnum If provided, only packets with this port number will be exported.
|
||||
*/
|
||||
fun saveDataCsv(uri: MeshtasticUri, filterPortnum: Int? = null) {
|
||||
fun saveDataCsv(uri: CommonUri, filterPortnum: Int? = null) {
|
||||
safeLaunch(tag = "saveDataCsv") {
|
||||
fileService.write(uri) { writer -> performDataExport(writer, filterPortnum) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -158,7 +158,7 @@ fun DebugSearchState(
|
|||
onExportLogs: (() -> Unit)? = null,
|
||||
) {
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
var customFilterText by remember { mutableStateOf("") }
|
||||
var customFilterText by rememberSaveable { mutableStateOf("") }
|
||||
|
||||
Column(modifier = modifier.background(color = colorScheme.background.copy(alpha = 1.0f)).padding(8.dp)) {
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -61,15 +61,6 @@ import org.meshtastic.proto.Telemetry
|
|||
import org.meshtastic.proto.User
|
||||
import org.meshtastic.proto.Waypoint
|
||||
|
||||
data class SearchMatch(val logIndex: Int, val start: Int, val end: Int, val field: String)
|
||||
|
||||
data class SearchState(
|
||||
val searchText: String = "",
|
||||
val currentMatchIndex: Int = -1,
|
||||
val allMatches: List<SearchMatch> = emptyList(),
|
||||
val hasMatches: Boolean = false,
|
||||
)
|
||||
|
||||
enum class FilterMode {
|
||||
AND,
|
||||
OR,
|
||||
|
|
@ -387,17 +378,15 @@ class DebugViewModel(
|
|||
val nodeIdStr = nodeId.toUInt().toString()
|
||||
// Only match if whitespace before and after
|
||||
val regex = Regex("""(?<=\s|^)${Regex.escape(nodeIdStr)}(?=\s|$)""")
|
||||
regex.find(this)?.let { _ ->
|
||||
regex.findAll(this).toList().asReversed().forEach {
|
||||
val idx = it.range.last + 1
|
||||
insert(idx, " (${nodeId.toHex(8)})")
|
||||
}
|
||||
return true
|
||||
if (!regex.containsMatchIn(this)) return false
|
||||
regex.findAll(this).toList().asReversed().forEach {
|
||||
val idx = it.range.last + 1
|
||||
insert(idx, " (${nodeId.toHex(8)})")
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
private fun Int.toHex(length: Int): String = "!" + this.toUInt().toString(16).padStart(length, '0')
|
||||
private fun Int.toHex(length: Int): String = "!${this.toUInt().toString(16).padStart(length, '0')}"
|
||||
|
||||
fun requestDeleteAllLogs() {
|
||||
alertManager.showAlert(
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package org.meshtastic.feature.settings.navigation
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
|
|
@ -80,7 +79,7 @@ fun getRadioConfigViewModel(backStack: NavBackStack<NavKey>): RadioConfigViewMod
|
|||
.lastOrNull { it is SettingsRoute.SettingsGraph }
|
||||
?.let { (it as SettingsRoute.SettingsGraph).destNum }
|
||||
}
|
||||
SideEffect { viewModel.initDestNum(destNum) }
|
||||
LaunchedEffect(destNum) { viewModel.initDestNum(destNum) }
|
||||
return viewModel
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.update
|
|||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.koin.core.annotation.InjectedParam
|
||||
import org.koin.core.annotation.KoinViewModel
|
||||
import org.meshtastic.core.common.util.MeshtasticUri
|
||||
import org.meshtastic.core.common.util.CommonUri
|
||||
import org.meshtastic.core.domain.usecase.settings.AdminActionsUseCase
|
||||
import org.meshtastic.core.domain.usecase.settings.ExportProfileUseCase
|
||||
import org.meshtastic.core.domain.usecase.settings.ExportSecurityConfigUseCase
|
||||
|
|
@ -384,7 +384,7 @@ open class RadioConfigViewModel(
|
|||
safeLaunch(tag = "removeFixedPosition") { radioConfigUseCase.removeFixedPosition(destNum) }
|
||||
}
|
||||
|
||||
fun importProfile(uri: MeshtasticUri, onResult: (DeviceProfile) -> Unit) {
|
||||
fun importProfile(uri: CommonUri, onResult: (DeviceProfile) -> Unit) {
|
||||
safeLaunch(tag = "importProfile") {
|
||||
var profile: DeviceProfile? = null
|
||||
fileService.read(uri) { source ->
|
||||
|
|
@ -394,7 +394,7 @@ open class RadioConfigViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun exportProfile(uri: MeshtasticUri, profile: DeviceProfile) {
|
||||
fun exportProfile(uri: CommonUri, profile: DeviceProfile) {
|
||||
safeLaunch(tag = "exportProfile") {
|
||||
fileService.write(uri) { sink ->
|
||||
exportProfileUseCase(sink, profile).onSuccess { /* Success */ }.onFailure { throw it }
|
||||
|
|
@ -402,7 +402,7 @@ open class RadioConfigViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
fun exportSecurityConfig(uri: MeshtasticUri, securityConfig: Config.SecurityConfig) {
|
||||
fun exportSecurityConfig(uri: CommonUri, securityConfig: Config.SecurityConfig) {
|
||||
safeLaunch(tag = "exportSecurityConfig") {
|
||||
fileService.write(uri) { sink ->
|
||||
exportSecurityConfigUseCase(sink, securityConfig).onSuccess { /* Success */ }.onFailure { throw it }
|
||||
|
|
|
|||
|
|
@ -113,9 +113,9 @@ private fun ChannelConfigScreen(
|
|||
onPositiveClicked: (List<ChannelSettings>) -> Unit,
|
||||
) {
|
||||
val primarySettings = settingsList.getOrNull(0) ?: return
|
||||
val modemPresetName by remember(loraConfig) { mutableStateOf(Channel(loraConfig = loraConfig).name) }
|
||||
val primaryChannel by remember(loraConfig) { mutableStateOf(Channel(primarySettings, loraConfig)) }
|
||||
val capabilities by remember(firmwareVersion) { mutableStateOf(Capabilities(firmwareVersion)) }
|
||||
val modemPresetName = remember(loraConfig) { Channel(loraConfig = loraConfig).name }
|
||||
val primaryChannel = remember(loraConfig) { Channel(primarySettings, loraConfig) }
|
||||
val capabilities = remember(firmwareVersion) { Capabilities(firmwareVersion) }
|
||||
|
||||
val focusManager = LocalFocusManager.current
|
||||
val settingsListInput =
|
||||
|
|
@ -141,7 +141,7 @@ private fun ChannelConfigScreen(
|
|||
if (showEditChannelDialog != null) {
|
||||
val index = showEditChannelDialog ?: return
|
||||
EditChannelDialog(
|
||||
channelSettings = with(settingsListInput) { if (size > index) get(index) else ChannelSettings() },
|
||||
channelSettings = settingsListInput.getOrNull(index) ?: ChannelSettings(),
|
||||
modemPresetName = modemPresetName,
|
||||
onAddClick = {
|
||||
if (settingsListInput.size > index) {
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ fun ChannelScreen(
|
|||
val modemPresetName by
|
||||
remember(channels) { mutableStateOf(Channel(loraConfig = channels.lora_config ?: Config.LoRaConfig()).name) }
|
||||
|
||||
var showResetDialog by remember { mutableStateOf(false) }
|
||||
var showResetDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
var shouldAddChannelsState by remember { mutableStateOf(true) }
|
||||
|
||||
|
|
@ -211,7 +211,7 @@ fun ChannelScreen(
|
|||
|
||||
requestChannelSet?.let { ScannedQrCodeDialog(it, onDismiss = { viewModel.clearRequestChannelUrl() }) }
|
||||
|
||||
var showShareDialog by remember { mutableStateOf(false) }
|
||||
var showShareDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
if (showShareDialog) {
|
||||
ChannelShareDialog(
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit) {
|
|||
val primarySettings = state.channelList.getOrNull(0) ?: return
|
||||
val formState = rememberConfigState(initialValue = loraConfig)
|
||||
|
||||
val primaryChannel by remember(formState.value) { mutableStateOf(Channel(primarySettings, formState.value)) }
|
||||
val primaryChannel = remember(formState.value) { Channel(primarySettings, formState.value) }
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
RadioConfigScreenList(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue