mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(core/ui): add safeLaunch, UiState, KMP permissions, and CMP lifecycle modernization (#5118)
This commit is contained in:
parent
27367e9064
commit
e46a8296cb
26 changed files with 374 additions and 184 deletions
|
|
@ -18,7 +18,6 @@ package org.meshtastic.feature.node.compass
|
|||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -26,7 +25,6 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.annotation.KoinViewModel
|
||||
import org.meshtastic.core.common.util.bearing
|
||||
import org.meshtastic.core.common.util.formatString
|
||||
|
|
@ -37,6 +35,7 @@ import org.meshtastic.core.di.CoroutineDispatchers
|
|||
import org.meshtastic.core.model.Node
|
||||
import org.meshtastic.core.model.util.toDistanceString
|
||||
import org.meshtastic.core.ui.component.precisionBitsToMeters
|
||||
import org.meshtastic.core.ui.viewmodel.safeLaunch
|
||||
import org.meshtastic.proto.Config
|
||||
import org.meshtastic.proto.Position
|
||||
import kotlin.math.abs
|
||||
|
|
@ -92,13 +91,17 @@ class CompassViewModel(
|
|||
|
||||
updatesJob?.cancel()
|
||||
|
||||
updatesJob = viewModelScope.launch {
|
||||
combine(headingProvider.headingUpdates(), phoneLocationProvider.locationUpdates()) { heading, location ->
|
||||
buildState(heading, location)
|
||||
updatesJob =
|
||||
safeLaunch(tag = "compassUpdates") {
|
||||
combine(headingProvider.headingUpdates(), phoneLocationProvider.locationUpdates()) {
|
||||
heading,
|
||||
location,
|
||||
->
|
||||
buildState(heading, location)
|
||||
}
|
||||
.flowOn(dispatchers.default)
|
||||
.collect { _uiState.value = it }
|
||||
}
|
||||
.flowOn(dispatchers.default)
|
||||
.collect { _uiState.value = it }
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ import kotlinx.coroutines.flow.flatMapLatest
|
|||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import okio.ByteString.Companion.decodeBase64
|
||||
|
|
@ -60,6 +59,7 @@ import org.meshtastic.core.resources.traceroute
|
|||
import org.meshtastic.core.resources.view_on_map
|
||||
import org.meshtastic.core.ui.util.AlertManager
|
||||
import org.meshtastic.core.ui.util.toMessageRes
|
||||
import org.meshtastic.core.ui.viewmodel.safeLaunch
|
||||
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
import org.meshtastic.feature.node.detail.NodeRequestActions
|
||||
import org.meshtastic.feature.node.domain.usecase.GetNodeDetailsUseCase
|
||||
|
|
@ -181,7 +181,8 @@ open class MetricsViewModel(
|
|||
|
||||
fun getUser(nodeNum: Int) = nodeRepository.getUser(nodeNum)
|
||||
|
||||
fun deleteLog(uuid: String) = viewModelScope.launch(dispatchers.io) { meshLogRepository.deleteLog(uuid) }
|
||||
fun deleteLog(uuid: String) =
|
||||
safeLaunch(context = dispatchers.io, tag = "deleteLog") { meshLogRepository.deleteLog(uuid) }
|
||||
|
||||
fun getTracerouteOverlay(requestId: Int): TracerouteOverlay? {
|
||||
val cached = tracerouteOverlayCache.value[requestId]
|
||||
|
|
@ -216,7 +217,7 @@ open class MetricsViewModel(
|
|||
private fun List<Node>.numSet(): Set<Int> = map { it.num }.toSet()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
safeLaunch(tag = "tracerouteCollector") {
|
||||
serviceRepository.tracerouteResponse.filterNotNull().collect { response ->
|
||||
val overlay =
|
||||
TracerouteOverlay(
|
||||
|
|
@ -232,7 +233,7 @@ open class MetricsViewModel(
|
|||
Logger.d { "MetricsViewModel created" }
|
||||
}
|
||||
|
||||
fun clearPosition() = viewModelScope.launch(dispatchers.io) {
|
||||
fun clearPosition() = safeLaunch(context = dispatchers.io, tag = "clearPosition") {
|
||||
(manualNodeId.value ?: nodeIdFromRoute)?.let {
|
||||
meshLogRepository.deleteLogs(it, PortNum.POSITION_APP.value)
|
||||
}
|
||||
|
|
@ -276,7 +277,7 @@ open class MetricsViewModel(
|
|||
overlay: TracerouteOverlay?,
|
||||
onViewOnMap: (Int, String) -> Unit,
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
safeLaunch(tag = "showTracerouteDetail") {
|
||||
val snapshotPositions = tracerouteSnapshotRepository.getSnapshotPositions(responseLogUuid).first()
|
||||
alertManager.showAlert(
|
||||
titleRes = Res.string.traceroute,
|
||||
|
|
@ -299,7 +300,7 @@ open class MetricsViewModel(
|
|||
if (errorRes != null) {
|
||||
// Post the error alert after the current alert is dismissed to avoid
|
||||
// the wrapping dismissAlert() in AlertManager immediately clearing it.
|
||||
viewModelScope.launch {
|
||||
safeLaunch(tag = "tracerouteError") {
|
||||
alertManager.showAlert(titleRes = Res.string.traceroute, messageRes = errorRes)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -336,7 +337,7 @@ open class MetricsViewModel(
|
|||
epochSeconds: (T) -> Long,
|
||||
rowMapper: (T) -> String,
|
||||
) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
safeLaunch(context = dispatchers.io, tag = "exportCsv") {
|
||||
fileService.write(uri) { sink ->
|
||||
sink.writeUtf8(header)
|
||||
rows.forEach { item ->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue