Add fixes to navigation.

This commit is contained in:
Sylwester Zieliński
2022-01-19 15:54:25 +01:00
parent 2dd3e4bec3
commit 98e5b530d6
13 changed files with 91 additions and 96 deletions

View File

@@ -5,12 +5,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.navigation.ForwardDestination
import no.nordicsemi.android.navigation.NavigationManager
import no.nordicsemi.android.navigation.UUIDArgument
import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
import javax.inject.Inject
@HiltViewModel
class HomeViewModel @Inject constructor(
private val deviceHolder: SelectedBluetoothDeviceHolder,
private val navigationManager: NavigationManager
) : ViewModel() {

View File

@@ -24,9 +24,7 @@ class MainActivity : NordicActivity() {
color = MaterialTheme.colorScheme.surface,
modifier = Modifier.fillMaxSize()
) {
NavigationView(
HomeDestinations + ProfileDestinations + ScannerDestinations
)
NavigationView(HomeDestinations + ProfileDestinations + ScannerDestinations)
}
}
}

View File

@@ -35,7 +35,6 @@ import kotlinx.coroutines.flow.asStateFlow
import no.nordicsemi.android.ble.BleManager
import no.nordicsemi.android.log.ILogSession
import no.nordicsemi.android.log.Logger
import javax.inject.Inject
@AndroidEntryPoint
abstract class BleProfileService : Service() {
@@ -47,9 +46,6 @@ abstract class BleProfileService : Service() {
private val _status = MutableStateFlow(BleManagerStatus.CONNECTING)
val status = _status.asStateFlow()
@Inject
lateinit var bluetoothDeviceHolder: SelectedBluetoothDeviceHolder
/**
* Returns a handler that is created in onCreate().
* The handler may be used to postpone execution of some operations or to run them in UI thread.
@@ -58,17 +54,6 @@ abstract class BleProfileService : Service() {
private var activityIsChangingConfiguration = false
/**
* Returns the Bluetooth device object
*
* @return bluetooth device
*/
private val bluetoothDevice: BluetoothDevice by lazy {
bluetoothDeviceHolder.device?.device ?: throw IllegalArgumentException(
"No device associated with the application."
)
}
/**
* Returns the log session that can be used to append log entries. The method returns `null` if the nRF Logger app was not installed. It is safe to use logger when
* [.onServiceStarted] has been called.
@@ -119,7 +104,7 @@ abstract class BleProfileService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
manager.connect(bluetoothDevice)
manager.connect(intent!!.getParcelableExtra(DEVICE_DATA)!!)
.useAutoConnect(shouldAutoConnect())
.retry(3, 100)
.enqueue()
@@ -141,7 +126,6 @@ abstract class BleProfileService : Service() {
// shutdown the manager
manager.disconnect().enqueue()
Logger.i(logSession, "Service destroyed")
bluetoothDeviceHolder.forgetDevice()
logSession = null
handler = null
}
@@ -184,14 +168,6 @@ abstract class BleProfileService : Service() {
}
}
/**
* Returns the device address
*
* @return device address
*/
protected val deviceAddress: String
get() = bluetoothDevice.address
/**
* Returns `true` if the device is connected to the sensor.
*

View File

@@ -1,20 +0,0 @@
package no.nordicsemi.android.service
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SelectedBluetoothDeviceHolder @Inject constructor() {
var device: DiscoveredBluetoothDevice? = null
private set
fun attachDevice(newDevice: DiscoveredBluetoothDevice) {
device = newDevice
}
fun forgetDevice() {
device = null
}
}

View File

@@ -0,0 +1,22 @@
package no.nordicsemi.android.service
import android.content.Context
import android.content.Intent
import dagger.hilt.android.qualifiers.ApplicationContext
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import javax.inject.Inject
const val DEVICE_DATA = "device-data"
class ServiceManager @Inject constructor(
@ApplicationContext
private val context: Context
) {
fun <T> startService(service: Class<T>, device: DiscoveredBluetoothDevice) {
val intent = Intent(context, service).apply {
putExtra(DEVICE_DATA, device)
}
context.startService(intent)
}
}

View File

@@ -16,20 +16,27 @@ import no.nordicsemi.android.bps.view.DisconnectEvent
import no.nordicsemi.android.bps.view.DisplayDataState
import no.nordicsemi.android.bps.view.LoadingState
import no.nordicsemi.android.navigation.NavigationManager
import no.nordicsemi.android.navigation.ParcelableArgument
import no.nordicsemi.android.navigation.SuccessDestinationResult
import no.nordicsemi.android.service.BleManagerStatus
import no.nordicsemi.android.service.ConnectionObserverAdapter
import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import no.nordicsemi.ui.scanner.ScannerDestinationId
import javax.inject.Inject
@HiltViewModel
internal class BPSViewModel @Inject constructor(
private val bpsManager: BPSManager,
private val deviceHolder: SelectedBluetoothDeviceHolder,
private val repository: BPSRepository,
private val navigationManager: NavigationManager
) : ViewModel() {
private val args
get() = navigationManager.getResult(ScannerDestinationId)
private val device
get() = ((args as SuccessDestinationResult).argument as ParcelableArgument).value as DiscoveredBluetoothDevice
val state = repository.data.combine(repository.status) { data, status ->
when (status) {
BleManagerStatus.CONNECTING -> LoadingState
@@ -70,7 +77,7 @@ internal class BPSViewModel @Inject constructor(
}
fun connectDevice() {
bpsManager.connect(deviceHolder.device!!.device)
bpsManager.connect(device.device)
.useAutoConnect(false)
.retry(3, 100)
.enqueue()
@@ -78,7 +85,6 @@ internal class BPSViewModel @Inject constructor(
private fun onDisconnectButtonClick() {
bpsManager.disconnect().enqueue()
deviceHolder.forgetDevice()
repository.clear()
}

View File

@@ -11,6 +11,7 @@ dependencies {
implementation libs.nordic.log
implementation libs.nordic.theme
implementation libs.nordic.navigation
implementation libs.nordic.ui.scanner
implementation libs.bundles.compose
implementation libs.androidx.core

View File

@@ -1,15 +1,11 @@
package no.nordicsemi.android.csc.view
import android.content.Intent
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import no.nordicsemi.android.csc.R
import no.nordicsemi.android.csc.repository.CSCService
import no.nordicsemi.android.csc.viewmodel.CSCViewModel
import no.nordicsemi.android.theme.view.BackIconAppBar
import no.nordicsemi.android.theme.view.DeviceConnectingView
@@ -20,24 +16,13 @@ fun CSCScreen() {
val viewModel: CSCViewModel = hiltViewModel()
val state = viewModel.state.collectAsState().value
val context = LocalContext.current
LaunchedEffect(Unit) {
val intent = Intent(context, CSCService::class.java)
context.startService(intent)
}
CSCView(state) { viewModel.onEvent(it) }
}
@Composable
private fun CSCView(state: CSCViewState, onEvent: (CSCViewEvent) -> Unit) {
Column {
BackIconAppBar(stringResource(id = R.string.csc_title)) {
onEvent(OnDisconnectButtonClick)
viewModel.onEvent(OnDisconnectButtonClick)
}
when (state) {
is DisplayDataState -> CSCContentView(state.data, onEvent)
is DisplayDataState -> CSCContentView(state.data) { viewModel.onEvent(it) }
LoadingState -> DeviceConnectingView()
}.exhaustive
}

View File

@@ -11,23 +11,37 @@ import kotlinx.coroutines.flow.stateIn
import no.nordicsemi.android.csc.data.CSCRepository
import no.nordicsemi.android.csc.data.DisconnectCommand
import no.nordicsemi.android.csc.data.SetWheelSizeCommand
import no.nordicsemi.android.csc.repository.CSCService
import no.nordicsemi.android.csc.view.CSCViewEvent
import no.nordicsemi.android.csc.view.DisplayDataState
import no.nordicsemi.android.csc.view.LoadingState
import no.nordicsemi.android.csc.view.OnDisconnectButtonClick
import no.nordicsemi.android.csc.view.OnSelectedSpeedUnitSelected
import no.nordicsemi.android.csc.view.OnWheelSizeSelected
import no.nordicsemi.android.navigation.CancelDestinationResult
import no.nordicsemi.android.navigation.ForwardDestination
import no.nordicsemi.android.navigation.NavigationManager
import no.nordicsemi.android.navigation.ParcelableArgument
import no.nordicsemi.android.navigation.SuccessDestinationResult
import no.nordicsemi.android.service.BleManagerStatus
import no.nordicsemi.android.service.ServiceManager
import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import no.nordicsemi.ui.scanner.ScannerDestinationId
import javax.inject.Inject
@HiltViewModel
internal class CSCViewModel @Inject constructor(
private val repository: CSCRepository,
private val serviceManager: ServiceManager,
private val navigationManager: NavigationManager
) : ViewModel() {
private val args
get() = navigationManager.getResult(ScannerDestinationId)
private val device
get() = ((args as SuccessDestinationResult).argument as ParcelableArgument).value as DiscoveredBluetoothDevice
val state = repository.data.combine(repository.status) { data, status ->
when (status) {
BleManagerStatus.CONNECTING -> LoadingState
@@ -37,6 +51,12 @@ internal class CSCViewModel @Inject constructor(
}.stateIn(viewModelScope, SharingStarted.Lazily, LoadingState)
init {
when (args) {
CancelDestinationResult -> navigationManager.navigateUp()
is SuccessDestinationResult -> serviceManager.startService(CSCService::class.java, device)
null -> navigationManager.navigateTo(ForwardDestination(ScannerDestinationId))
}.exhaustive
repository.status.onEach {
if (it == BleManagerStatus.DISCONNECTED) {
navigationManager.navigateUp()

View File

@@ -3,19 +3,16 @@ package no.nordicsemi.dfu.data
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import no.nordicsemi.android.dfu.DfuServiceInitiator
import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
import no.nordicsemi.dfu.repository.DFUService
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import javax.inject.Inject
class DFUManager @Inject constructor(
@ApplicationContext
private val context: Context,
private val deviceHolder: SelectedBluetoothDeviceHolder
private val context: Context
) {
fun install(file: ZipFile) {
val device = deviceHolder.device!!
fun install(file: ZipFile, device: DiscoveredBluetoothDevice) {
val starter = DfuServiceInitiator(device.address())
.setDeviceName(device.displayName())
// .setKeepBond(keepBond)

View File

@@ -8,13 +8,12 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import no.nordicsemi.android.service.BleManagerStatus
import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class DFURepository @Inject constructor(
private val deviceHolder: SelectedBluetoothDeviceHolder,
private val fileManger: DFUFileManager
) {
@@ -27,10 +26,10 @@ internal class DFURepository @Inject constructor(
private val _status = MutableStateFlow(BleManagerStatus.CONNECTING)
val status = _status.asStateFlow()
fun setZipFile(file: Uri) {
fun setZipFile(file: Uri, device: DiscoveredBluetoothDevice) {
val currentState = _data.value as NoFileSelectedState
_data.value = fileManger.createFile(file)?.let {
FileReadyState(it, requireNotNull(deviceHolder.device))
FileReadyState(it, device)
} ?: currentState.copy(isError = true)
}

View File

@@ -6,8 +6,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import no.nordicsemi.android.navigation.NavigationManager
import no.nordicsemi.android.navigation.ParcelableArgument
import no.nordicsemi.android.navigation.SuccessDestinationResult
import no.nordicsemi.android.service.BleManagerStatus
import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.dfu.data.Completed
import no.nordicsemi.dfu.data.DFUManager
@@ -28,16 +30,24 @@ import no.nordicsemi.dfu.view.OnInstallButtonClick
import no.nordicsemi.dfu.view.OnPauseButtonClick
import no.nordicsemi.dfu.view.OnStopButtonClick
import no.nordicsemi.dfu.view.OnZipFileSelected
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import no.nordicsemi.ui.scanner.ScannerDestinationId
import javax.inject.Inject
@HiltViewModel
internal class DFUViewModel @Inject constructor(
private val repository: DFURepository,
private val progressManager: DFUProgressManager,
private val deviceHolder: SelectedBluetoothDeviceHolder,
private val dfuManager: DFUManager
private val dfuManager: DFUManager,
private val navigationManager: NavigationManager
) : ViewModel() {
private val args
get() = navigationManager.getResult(ScannerDestinationId)
private val device
get() = ((args as SuccessDestinationResult).argument as ParcelableArgument).value as DiscoveredBluetoothDevice
val state = repository.data.combine(progressManager.status) { state, status ->
(state as? FileInstallingState)
?.run { createInstallingStateWithNewStatus(state, status) }
@@ -58,19 +68,18 @@ internal class DFUViewModel @Inject constructor(
when (event) {
OnDisconnectButtonClick -> closeScreen()
OnInstallButtonClick -> {
dfuManager.install(requireFile())
dfuManager.install(requireFile(), device)
repository.install()
}
OnPauseButtonClick -> closeScreen()
OnStopButtonClick -> closeScreen()
is OnZipFileSelected -> repository.setZipFile(event.file)
is OnZipFileSelected -> repository.setZipFile(event.file, device)
}.exhaustive
}
private fun closeScreen() {
repository.sendNewCommand(DisconnectCommand)
repository.clear()
deviceHolder.forgetDevice()
}
private fun requireFile(): ZipFile {

View File

@@ -15,20 +15,27 @@ import no.nordicsemi.android.gls.repository.GLSManager
import no.nordicsemi.android.gls.view.DisplayDataState
import no.nordicsemi.android.gls.view.LoadingState
import no.nordicsemi.android.navigation.NavigationManager
import no.nordicsemi.android.navigation.ParcelableArgument
import no.nordicsemi.android.navigation.SuccessDestinationResult
import no.nordicsemi.android.service.BleManagerStatus
import no.nordicsemi.android.service.ConnectionObserverAdapter
import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import no.nordicsemi.ui.scanner.ScannerDestinationId
import javax.inject.Inject
@HiltViewModel
internal class GLSViewModel @Inject constructor(
private val glsManager: GLSManager,
private val deviceHolder: SelectedBluetoothDeviceHolder,
private val repository: GLSRepository,
private val navigationManager: NavigationManager
) : ViewModel() {
private val args
get() = navigationManager.getResult(ScannerDestinationId)
private val device
get() = ((args as SuccessDestinationResult).argument as ParcelableArgument).value as DiscoveredBluetoothDevice
val state = repository.data.combine(repository.status) { data, status ->
when (status) {
BleManagerStatus.CONNECTING -> LoadingState
@@ -70,12 +77,10 @@ internal class GLSViewModel @Inject constructor(
}
fun connectDevice() {
deviceHolder.device?.let {
glsManager.connect(it.device)
.useAutoConnect(false)
.retry(3, 100)
.enqueue()
}
glsManager.connect(device.device)
.useAutoConnect(false)
.retry(3, 100)
.enqueue()
}
private fun requestData(mode: WorkingMode) {
@@ -87,7 +92,6 @@ internal class GLSViewModel @Inject constructor(
}
private fun disconnect() {
deviceHolder.forgetDevice()
glsManager.disconnect().enqueue()
}