kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
Flatten `BluetoothViewModel` (#3138)
rodzic
eedc3ef963
commit
f2d29d4582
|
@ -42,7 +42,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.geeksville.mesh.android.GeeksvilleApplication
|
import com.geeksville.mesh.android.GeeksvilleApplication
|
||||||
import com.geeksville.mesh.android.Logging
|
import com.geeksville.mesh.android.Logging
|
||||||
import com.geeksville.mesh.android.prefs.UiPrefs
|
import com.geeksville.mesh.android.prefs.UiPrefs
|
||||||
import com.geeksville.mesh.model.BluetoothViewModel
|
|
||||||
import com.geeksville.mesh.model.UIViewModel
|
import com.geeksville.mesh.model.UIViewModel
|
||||||
import com.geeksville.mesh.navigation.DEEP_LINK_BASE_URI
|
import com.geeksville.mesh.navigation.DEEP_LINK_BASE_URI
|
||||||
import com.geeksville.mesh.ui.MainScreen
|
import com.geeksville.mesh.ui.MainScreen
|
||||||
|
@ -58,7 +57,6 @@ import javax.inject.Inject
|
||||||
class MainActivity :
|
class MainActivity :
|
||||||
AppCompatActivity(),
|
AppCompatActivity(),
|
||||||
Logging {
|
Logging {
|
||||||
private val bluetoothViewModel: BluetoothViewModel by viewModels()
|
|
||||||
private val model: UIViewModel by viewModels()
|
private val model: UIViewModel by viewModels()
|
||||||
|
|
||||||
// This is aware of the Activity lifecycle and handles binding to the mesh service.
|
// This is aware of the Activity lifecycle and handles binding to the mesh service.
|
||||||
|
@ -108,7 +106,7 @@ class MainActivity :
|
||||||
|
|
||||||
val appIntroCompleted by model.appIntroCompleted.collectAsStateWithLifecycle()
|
val appIntroCompleted by model.appIntroCompleted.collectAsStateWithLifecycle()
|
||||||
if (appIntroCompleted) {
|
if (appIntroCompleted) {
|
||||||
MainScreen(uIViewModel = model, bluetoothViewModel = bluetoothViewModel)
|
MainScreen(uIViewModel = model)
|
||||||
} else {
|
} else {
|
||||||
AppIntroductionScreen(
|
AppIntroductionScreen(
|
||||||
onDone = {
|
onDone = {
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.geeksville.mesh.model
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
/** Thin view model which adapts the view layer to the `BluetoothRepository`. */
|
|
||||||
@HiltViewModel
|
|
||||||
class BluetoothViewModel @Inject constructor(private val bluetoothRepository: BluetoothRepository) : ViewModel() {
|
|
||||||
/** Called when permissions have been updated. This causes an explicit refresh of the bluetooth state. */
|
|
||||||
fun permissionsUpdated() = bluetoothRepository.refreshState()
|
|
||||||
|
|
||||||
val enabled = bluetoothRepository.state.map { it.enabled }
|
|
||||||
}
|
|
|
@ -24,12 +24,11 @@ import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.navDeepLink
|
import androidx.navigation.navDeepLink
|
||||||
import androidx.navigation.navigation
|
import androidx.navigation.navigation
|
||||||
import com.geeksville.mesh.model.BluetoothViewModel
|
|
||||||
import com.geeksville.mesh.ui.connections.ConnectionsScreen
|
import com.geeksville.mesh.ui.connections.ConnectionsScreen
|
||||||
import com.geeksville.mesh.ui.settings.radio.components.LoRaConfigScreen
|
import com.geeksville.mesh.ui.settings.radio.components.LoRaConfigScreen
|
||||||
|
|
||||||
/** Navigation graph for for the top level ConnectionsScreen - [ConnectionsRoutes.Connections]. */
|
/** Navigation graph for for the top level ConnectionsScreen - [ConnectionsRoutes.Connections]. */
|
||||||
fun NavGraphBuilder.connectionsGraph(navController: NavHostController, bluetoothViewModel: BluetoothViewModel) {
|
fun NavGraphBuilder.connectionsGraph(navController: NavHostController) {
|
||||||
@Suppress("ktlint:standard:max-line-length")
|
@Suppress("ktlint:standard:max-line-length")
|
||||||
navigation<ConnectionsRoutes.ConnectionsGraph>(startDestination = ConnectionsRoutes.Connections) {
|
navigation<ConnectionsRoutes.ConnectionsGraph>(startDestination = ConnectionsRoutes.Connections) {
|
||||||
composable<ConnectionsRoutes.Connections>(
|
composable<ConnectionsRoutes.Connections>(
|
||||||
|
@ -40,7 +39,6 @@ fun NavGraphBuilder.connectionsGraph(navController: NavHostController, bluetooth
|
||||||
val parentEntry =
|
val parentEntry =
|
||||||
remember(backStackEntry) { navController.getBackStackEntry(ConnectionsRoutes.ConnectionsGraph) }
|
remember(backStackEntry) { navController.getBackStackEntry(ConnectionsRoutes.ConnectionsGraph) }
|
||||||
ConnectionsScreen(
|
ConnectionsScreen(
|
||||||
bluetoothViewModel = bluetoothViewModel,
|
|
||||||
radioConfigViewModel = hiltViewModel(parentEntry),
|
radioConfigViewModel = hiltViewModel(parentEntry),
|
||||||
onClickNodeChip = {
|
onClickNodeChip = {
|
||||||
navController.navigate(NodesRoutes.NodeDetailGraph(it)) {
|
navController.navigate(NodesRoutes.NodeDetailGraph(it)) {
|
||||||
|
|
|
@ -81,7 +81,6 @@ import com.geeksville.mesh.android.AddNavigationTracking
|
||||||
import com.geeksville.mesh.android.BuildUtils.debug
|
import com.geeksville.mesh.android.BuildUtils.debug
|
||||||
import com.geeksville.mesh.android.setAttributes
|
import com.geeksville.mesh.android.setAttributes
|
||||||
import com.geeksville.mesh.model.BTScanModel
|
import com.geeksville.mesh.model.BTScanModel
|
||||||
import com.geeksville.mesh.model.BluetoothViewModel
|
|
||||||
import com.geeksville.mesh.model.DeviceVersion
|
import com.geeksville.mesh.model.DeviceVersion
|
||||||
import com.geeksville.mesh.model.Node
|
import com.geeksville.mesh.model.Node
|
||||||
import com.geeksville.mesh.model.UIViewModel
|
import com.geeksville.mesh.model.UIViewModel
|
||||||
|
@ -148,11 +147,7 @@ enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector,
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class)
|
||||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(
|
fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanModel = hiltViewModel()) {
|
||||||
uIViewModel: UIViewModel = hiltViewModel(),
|
|
||||||
bluetoothViewModel: BluetoothViewModel = hiltViewModel(),
|
|
||||||
scanModel: BTScanModel = hiltViewModel(),
|
|
||||||
) {
|
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val connectionState by uIViewModel.connectionState.collectAsStateWithLifecycle()
|
val connectionState by uIViewModel.connectionState.collectAsStateWithLifecycle()
|
||||||
val requestChannelSet by uIViewModel.requestChannelSet.collectAsStateWithLifecycle()
|
val requestChannelSet by uIViewModel.requestChannelSet.collectAsStateWithLifecycle()
|
||||||
|
@ -396,7 +391,7 @@ fun MainScreen(
|
||||||
nodesGraph(navController, uiViewModel = uIViewModel)
|
nodesGraph(navController, uiViewModel = uIViewModel)
|
||||||
mapGraph(navController, uiViewModel = uIViewModel)
|
mapGraph(navController, uiViewModel = uIViewModel)
|
||||||
channelsGraph(navController, uiViewModel = uIViewModel)
|
channelsGraph(navController, uiViewModel = uIViewModel)
|
||||||
connectionsGraph(navController, bluetoothViewModel)
|
connectionsGraph(navController)
|
||||||
settingsGraph(navController)
|
settingsGraph(navController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.geeksville.mesh.ConfigProtos
|
import com.geeksville.mesh.ConfigProtos
|
||||||
import com.geeksville.mesh.R
|
import com.geeksville.mesh.R
|
||||||
import com.geeksville.mesh.model.BTScanModel
|
import com.geeksville.mesh.model.BTScanModel
|
||||||
import com.geeksville.mesh.model.BluetoothViewModel
|
|
||||||
import com.geeksville.mesh.model.DeviceListEntry
|
import com.geeksville.mesh.model.DeviceListEntry
|
||||||
import com.geeksville.mesh.model.Node
|
import com.geeksville.mesh.model.Node
|
||||||
import com.geeksville.mesh.navigation.ConfigRoute
|
import com.geeksville.mesh.navigation.ConfigRoute
|
||||||
|
@ -102,7 +101,6 @@ fun String?.isIPAddress(): Boolean = if (Build.VERSION.SDK_INT < Build.VERSION_C
|
||||||
fun ConnectionsScreen(
|
fun ConnectionsScreen(
|
||||||
connectionsViewModel: ConnectionsViewModel = hiltViewModel(),
|
connectionsViewModel: ConnectionsViewModel = hiltViewModel(),
|
||||||
scanModel: BTScanModel = hiltViewModel(),
|
scanModel: BTScanModel = hiltViewModel(),
|
||||||
bluetoothViewModel: BluetoothViewModel = hiltViewModel(),
|
|
||||||
radioConfigViewModel: RadioConfigViewModel = hiltViewModel(),
|
radioConfigViewModel: RadioConfigViewModel = hiltViewModel(),
|
||||||
onClickNodeChip: (Int) -> Unit,
|
onClickNodeChip: (Int) -> Unit,
|
||||||
onNavigateToSettings: () -> Unit,
|
onNavigateToSettings: () -> Unit,
|
||||||
|
@ -120,7 +118,7 @@ fun ConnectionsScreen(
|
||||||
val info by connectionsViewModel.myNodeInfo.collectAsStateWithLifecycle()
|
val info by connectionsViewModel.myNodeInfo.collectAsStateWithLifecycle()
|
||||||
val ourNode by connectionsViewModel.ourNodeInfo.collectAsStateWithLifecycle()
|
val ourNode by connectionsViewModel.ourNodeInfo.collectAsStateWithLifecycle()
|
||||||
val selectedDevice by scanModel.selectedNotNullFlow.collectAsStateWithLifecycle()
|
val selectedDevice by scanModel.selectedNotNullFlow.collectAsStateWithLifecycle()
|
||||||
val bluetoothEnabled by bluetoothViewModel.enabled.collectAsStateWithLifecycle(false)
|
val bluetoothState by connectionsViewModel.bluetoothState.collectAsStateWithLifecycle()
|
||||||
val regionUnset = config.lora.region == ConfigProtos.Config.LoRaConfig.RegionCode.UNSET
|
val regionUnset = config.lora.region == ConfigProtos.Config.LoRaConfig.RegionCode.UNSET
|
||||||
|
|
||||||
val bleDevices by scanModel.bleDevicesForUi.collectAsStateWithLifecycle()
|
val bleDevices by scanModel.bleDevicesForUi.collectAsStateWithLifecycle()
|
||||||
|
@ -264,7 +262,7 @@ fun ConnectionsScreen(
|
||||||
btDevices = bleDevices,
|
btDevices = bleDevices,
|
||||||
selectedDevice = selectedDevice,
|
selectedDevice = selectedDevice,
|
||||||
scanModel = scanModel,
|
scanModel = scanModel,
|
||||||
bluetoothEnabled = bluetoothEnabled,
|
bluetoothEnabled = bluetoothState.enabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import com.geeksville.mesh.android.prefs.UiPrefs
|
||||||
import com.geeksville.mesh.database.NodeRepository
|
import com.geeksville.mesh.database.NodeRepository
|
||||||
import com.geeksville.mesh.database.entity.MyNodeEntity
|
import com.geeksville.mesh.database.entity.MyNodeEntity
|
||||||
import com.geeksville.mesh.model.Node
|
import com.geeksville.mesh.model.Node
|
||||||
|
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
|
||||||
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
|
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
@ -37,8 +38,9 @@ import javax.inject.Inject
|
||||||
class ConnectionsViewModel
|
class ConnectionsViewModel
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
private val radioConfigRepository: RadioConfigRepository,
|
radioConfigRepository: RadioConfigRepository,
|
||||||
private val nodeRepository: NodeRepository,
|
nodeRepository: NodeRepository,
|
||||||
|
bluetoothRepository: BluetoothRepository,
|
||||||
private val uiPrefs: UiPrefs,
|
private val uiPrefs: UiPrefs,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val localConfig: StateFlow<LocalConfig> =
|
val localConfig: StateFlow<LocalConfig> =
|
||||||
|
@ -48,14 +50,13 @@ constructor(
|
||||||
LocalConfig.getDefaultInstance(),
|
LocalConfig.getDefaultInstance(),
|
||||||
)
|
)
|
||||||
|
|
||||||
val connectionState
|
val connectionState = radioConfigRepository.connectionState
|
||||||
get() = radioConfigRepository.connectionState
|
|
||||||
|
|
||||||
val myNodeInfo: StateFlow<MyNodeEntity?>
|
val myNodeInfo: StateFlow<MyNodeEntity?> = nodeRepository.myNodeInfo
|
||||||
get() = nodeRepository.myNodeInfo
|
|
||||||
|
|
||||||
val ourNodeInfo: StateFlow<Node?>
|
val ourNodeInfo: StateFlow<Node?> = nodeRepository.ourNodeInfo
|
||||||
get() = nodeRepository.ourNodeInfo
|
|
||||||
|
val bluetoothState = bluetoothRepository.state
|
||||||
|
|
||||||
private val _hasShownNotPairedWarning = MutableStateFlow(uiPrefs.hasShownNotPairedWarning)
|
private val _hasShownNotPairedWarning = MutableStateFlow(uiPrefs.hasShownNotPairedWarning)
|
||||||
val hasShownNotPairedWarning: StateFlow<Boolean> = _hasShownNotPairedWarning.asStateFlow()
|
val hasShownNotPairedWarning: StateFlow<Boolean> = _hasShownNotPairedWarning.asStateFlow()
|
||||||
|
|
Ładowanie…
Reference in New Issue