From c15c3d8c092fb4f8ac77e4ed3f37673645da7832 Mon Sep 17 00:00:00 2001 From: andrekir Date: Mon, 15 Apr 2024 17:56:47 -0300 Subject: [PATCH] refactor(config): pass `destNum` via `setFragmentResultListener` --- .../java/com/geeksville/mesh/MainActivity.kt | 1 - .../mesh/model/RadioConfigViewModel.kt | 33 ++++++++++++------- .../java/com/geeksville/mesh/model/UIState.kt | 11 ------- .../datastore/RadioConfigRepository.kt | 5 ++- .../geeksville/mesh/service/MeshService.kt | 4 +++ .../mesh/ui/DeviceSettingsFragment.kt | 22 +++++++------ .../com/geeksville/mesh/ui/UsersFragment.kt | 4 ++- .../config/PositionConfigItemList.kt | 6 ++-- 8 files changed, 44 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index ac83f7f7..61ad9347 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -646,7 +646,6 @@ class MainActivity : AppCompatActivity(), Logging { return true } R.id.radio_config -> { - model.setDestNode(null) supportFragmentManager.beginTransaction() .add(R.id.mainActivityLayout, DeviceSettingsFragment()) .addToBackStack(null) diff --git a/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt b/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt index 3054c228..66c12309 100644 --- a/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt @@ -28,6 +28,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -44,8 +45,8 @@ data class RadioConfigState( val route: String = "", val userConfig: MeshProtos.User = MeshProtos.User.getDefaultInstance(), val channelList: List = emptyList(), - val radioConfig: ConfigProtos.Config = ConfigProtos.Config.getDefaultInstance(), - val moduleConfig: ModuleConfigProtos.ModuleConfig = ModuleConfigProtos.ModuleConfig.getDefaultInstance(), + val radioConfig: ConfigProtos.Config = config {}, + val moduleConfig: ModuleConfigProtos.ModuleConfig = moduleConfig {}, val ringtone: String = "", val cannedMessageMessages: String = "", val responseState: ResponseState = ResponseState.Empty, @@ -57,17 +58,22 @@ class RadioConfigViewModel @Inject constructor( private val radioConfigRepository: RadioConfigRepository, ) : ViewModel(), Logging { - private var destNum: Int = 0 private val meshService: IMeshService? get() = radioConfigRepository.meshService // Connection state to our radio device val connectionState get() = radioConfigRepository.connectionState - // A map from nodeNum to NodeInfo - val nodes: StateFlow> get() = radioConfigRepository.nodeDBbyNum + private val _destNum = MutableStateFlow(null) + private val _destNode = MutableStateFlow(null) + val destNode: StateFlow get() = _destNode - val myNodeInfo: StateFlow get() = radioConfigRepository.myNodeInfo - val ourNodeInfo: StateFlow get() = radioConfigRepository.ourNodeInfo + /** + * Sets the destination [NodeInfo] used in Radio Configuration. + * @param num Destination nodeNum (or null for our local [NodeInfo]). + */ + fun setDestNum(num: Int?) { + _destNum.value = num + } private val requestIds = MutableStateFlow(hashSetOf()) private val _radioConfigState = MutableStateFlow(RadioConfigState()) @@ -77,6 +83,10 @@ class RadioConfigViewModel @Inject constructor( val currentDeviceProfile get() = _currentDeviceProfile.value init { + combine(_destNum, radioConfigRepository.nodeDBbyNum) { destNum, nodes -> + nodes[destNum] ?: nodes.values.firstOrNull() + }.onEach { _destNode.value = it }.launchIn(viewModelScope) + radioConfigRepository.deviceProfileFlow.onEach { _currentDeviceProfile.value = it }.launchIn(viewModelScope) @@ -87,6 +97,7 @@ class RadioConfigViewModel @Inject constructor( debug("RadioConfigViewModel created") } + private val myNodeInfo: StateFlow get() = radioConfigRepository.myNodeInfo val myNodeNum get() = myNodeInfo.value?.myNodeNum val maxChannels get() = myNodeInfo.value?.maxChannels ?: 8 @@ -101,7 +112,6 @@ class RadioConfigViewModel @Inject constructor( errorMessage: String, ) = viewModelScope.launch { meshService?.let { service -> - this@RadioConfigViewModel.destNum = destNum val packetId = service.packetId try { requestAction(service, packetId, destNum) @@ -313,7 +323,7 @@ class RadioConfigViewModel @Inject constructor( fun installProfile(protobuf: DeviceProfile) = with(protobuf) { _deviceProfile.value = null // meshService?.beginEditSettings() - if (hasLongName() || hasShortName()) ourNodeInfo.value?.user?.let { + if (hasLongName() || hasShortName()) destNode.value?.user?.let { val user = it.copy( longName = if (hasLongName()) longName else it.longName, shortName = if (hasShortName()) shortName else it.shortName @@ -409,9 +419,8 @@ class RadioConfigViewModel @Inject constructor( if (data.requestId !in requestIds.value) return val route = radioConfigState.value.route - // val destNum = destNode.value?.num ?: return - val debugMsg = - "requestId: ${data.requestId.toUInt()} to: ${destNum.toUInt()} received %s from: ${packet.from.toUInt()}" + val destNum = destNode.value?.num ?: return + val debugMsg = "requestId: ${data.requestId.toUInt()} to: ${destNum.toUInt()} received %s" if (data?.portnumValue == Portnums.PortNum.ROUTING_APP_VALUE) { val parsed = MeshProtos.Routing.parseFrom(data.payload) diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index 35c805be..5ae7f223 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -229,17 +229,6 @@ class UIViewModel @Inject constructor( .filterValues { it.data.waypoint!!.expire > System.currentTimeMillis() / 1000 } }.asLiveData() - private val _destNode = MutableStateFlow(null) - val destNode: StateFlow get() = if (_destNode.value != null) _destNode else ourNodeInfo - - /** - * Sets the destination [NodeInfo] used in Radio Configuration. - * @param node Destination [NodeInfo] (or null for our local NodeInfo). - */ - fun setDestNode(node: NodeInfo?) { - _destNode.value = node - } - fun generatePacketId(): Int? { return try { meshService?.packetId diff --git a/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt index 50746495..77b6df16 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt @@ -143,14 +143,13 @@ class RadioConfigRepository @Inject constructor( * Flow representing the combined [DeviceProfile] protobuf. */ val deviceProfileFlow: Flow = combine( - myNodeInfoFlow(), nodeDBbyNum, channelSetFlow, localConfigFlow, moduleConfigFlow, - ) { myInfo, nodes, channels, localConfig, localModuleConfig -> + ) { nodes, channels, localConfig, localModuleConfig -> deviceProfile { - nodes[myInfo?.myNodeNum]?.user?.let { + nodes.values.firstOrNull()?.user?.let { longName = it.longName shortName = it.shortName } diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 8a9bcd6b..c6c4438b 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -1904,6 +1904,10 @@ class MeshService : Service(), Logging { removeFixedPosition = true } }) + + updateNodeInfo(myNodeNum) { + it.position = position.copy(time = currentSecond()) + } } } 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 4e96edbb..7e41f8e6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/DeviceSettingsFragment.kt @@ -48,7 +48,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat -import androidx.fragment.app.activityViewModels +import androidx.fragment.app.setFragmentResultListener +import androidx.fragment.app.viewModels import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController @@ -61,7 +62,6 @@ import com.geeksville.mesh.android.Logging import com.geeksville.mesh.config import com.geeksville.mesh.model.Channel import com.geeksville.mesh.model.RadioConfigViewModel -import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.moduleConfig import com.geeksville.mesh.service.MeshService.ConnectionState import com.geeksville.mesh.ui.components.PreferenceCategory @@ -95,18 +95,22 @@ import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class DeviceSettingsFragment : ScreenFragment("Radio Configuration"), Logging { - private val model: UIViewModel by activityViewModels() + private val model: RadioConfigViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { + setFragmentResultListener("requestKey") { _, bundle -> + val destNum = bundle.getInt("destNum") + model.setDestNum(destNum) + } + return ComposeView(requireContext()).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setBackgroundColor(ContextCompat.getColor(context, R.color.colorAdvancedBackground)) setContent { - // TODO change destNode to destNum and pass as navigation argument val node by model.destNode.collectAsStateWithLifecycle() AppCompatTheme { @@ -138,6 +142,7 @@ class DeviceSettingsFragment : ScreenFragment("Radio Configuration"), Logging { ) { innerPadding -> RadioConfigNavHost( node = node, + viewModel = model, navController = navController, modifier = Modifier.padding(innerPadding), ) @@ -231,12 +236,10 @@ fun RadioConfigNavHost( val connectionState by viewModel.connectionState.collectAsStateWithLifecycle() val connected = connectionState == ConnectionState.CONNECTED && node != null - val myNodeInfo by viewModel.myNodeInfo.collectAsStateWithLifecycle() // FIXME val destNum = node?.num ?: 0 - val isLocal = destNum == myNodeInfo?.myNodeNum + val isLocal = destNum == viewModel.myNodeNum val radioConfigState by viewModel.radioConfigState.collectAsStateWithLifecycle() - var location by remember(node) { mutableStateOf(node?.position) } // FIXME val deviceProfile by viewModel.deviceProfile.collectAsStateWithLifecycle() val isWaiting = radioConfigState.responseState.isWaiting() @@ -399,14 +402,13 @@ fun RadioConfigNavHost( composable(ConfigRoute.POSITION.name) { PositionConfigItemList( isLocal = isLocal, - location = location, + location = node?.position, positionConfig = radioConfigState.radioConfig.position, enabled = connected, onSaveClicked = { locationInput, positionInput -> if (positionInput.fixedPosition) { - if (locationInput != null && locationInput != location) { + if (locationInput != null && locationInput != node?.position) { viewModel.setFixedPosition(locationInput) - location = locationInput } } else { if (radioConfigState.radioConfig.position.fixedPosition) { 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 eb5775b0..3c45a879 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt @@ -18,7 +18,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.unit.dp +import androidx.core.os.bundleOf import androidx.fragment.app.activityViewModels +import androidx.fragment.app.setFragmentResult import androidx.lifecycle.asLiveData import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager @@ -171,7 +173,7 @@ class UsersFragment : ScreenFragment("Users"), Logging { } R.id.remote_admin -> { debug("calling remote admin --> destNum: ${node.num.toUInt()}") - model.setDestNode(node) + setFragmentResult("requestKey", bundleOf("destNum" to node.num)) parentFragmentManager.beginTransaction() .replace(R.id.mainActivityLayout, DeviceSettingsFragment()) .addToBackStack(null) diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/config/PositionConfigItemList.kt b/app/src/main/java/com/geeksville/mesh/ui/components/config/PositionConfigItemList.kt index 5a6c0056..3f20727c 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/config/PositionConfigItemList.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/config/PositionConfigItemList.kt @@ -32,8 +32,8 @@ fun PositionConfigItemList( onSaveClicked: (position: Position?, config: PositionConfig) -> Unit, ) { val focusManager = LocalFocusManager.current - var locationInput by remember { mutableStateOf(location) } - var positionInput by remember { mutableStateOf(positionConfig) } + var locationInput by remember(location) { mutableStateOf(location) } + var positionInput by remember(positionConfig) { mutableStateOf(positionConfig) } LazyColumn( modifier = Modifier.fillMaxSize() @@ -182,8 +182,6 @@ fun PositionConfigItemList( enabled = positionInput != positionConfig || locationInput != location, onCancelClicked = { focusManager.clearFocus() - locationInput = location - positionInput = positionConfig }, onSaveClicked = { focusManager.clearFocus()