Change HTS module

This commit is contained in:
Sylwester Zieliński
2022-02-11 15:36:58 +01:00
parent 771717224e
commit 382208454f
14 changed files with 213 additions and 162 deletions

View File

@@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.cgms.data.CGMRepository import no.nordicsemi.android.cgms.data.CGMRepository
import no.nordicsemi.android.csc.data.CSCRepository import no.nordicsemi.android.csc.data.CSCRepository
import no.nordicsemi.android.hrs.data.HRSRepository import no.nordicsemi.android.hrs.data.HRSRepository
import no.nordicsemi.android.hts.data.HTSRepository
import no.nordicsemi.android.navigation.NavigationManager import no.nordicsemi.android.navigation.NavigationManager
import no.nordicsemi.android.nrftoolbox.ProfileDestination import no.nordicsemi.android.nrftoolbox.ProfileDestination
import no.nordicsemi.android.nrftoolbox.view.HomeViewState import no.nordicsemi.android.nrftoolbox.view.HomeViewState
@@ -20,7 +21,8 @@ class HomeViewModel @Inject constructor(
private val navigationManager: NavigationManager, private val navigationManager: NavigationManager,
cgmRepository: CGMRepository, cgmRepository: CGMRepository,
cscRepository: CSCRepository, cscRepository: CSCRepository,
hrsRepository: HRSRepository hrsRepository: HRSRepository,
htsRepository: HTSRepository
) : ViewModel() { ) : ViewModel() {
private val _state = MutableStateFlow(HomeViewState()) private val _state = MutableStateFlow(HomeViewState())
@@ -38,6 +40,10 @@ class HomeViewModel @Inject constructor(
hrsRepository.isRunning.onEach { hrsRepository.isRunning.onEach {
_state.value = _state.value.copy(isHRSModuleRunning = it) _state.value = _state.value.copy(isHRSModuleRunning = it)
}.launchIn(viewModelScope) }.launchIn(viewModelScope)
htsRepository.isRunning.onEach {
_state.value = _state.value.copy(isHTSModuleRunning = it)
}.launchIn(viewModelScope)
} }
fun openProfile(destination: ProfileDestination) { fun openProfile(destination: ProfileDestination) {

View File

@@ -120,6 +120,7 @@ internal class HRSManager(
override fun onServicesInvalidated() { override fun onServicesInvalidated() {
bodySensorLocationCharacteristic = null bodySensorLocationCharacteristic = null
heartRateCharacteristic = null heartRateCharacteristic = null
batteryLevelCharacteristic = null
} }
} }
} }

View File

@@ -1,56 +1,6 @@
package no.nordicsemi.android.hts.data package no.nordicsemi.android.hts.data
import no.nordicsemi.android.material.you.RadioButtonItem
import no.nordicsemi.android.material.you.RadioGroupViewEntity
private const val DISPLAY_FAHRENHEIT = "°F"
private const val DISPLAY_CELSIUS = "°C"
private const val DISPLAY_KELVIN = "°K"
internal data class HTSData( internal data class HTSData(
val temperatureValue: Float = 0f, val temperatureValue: Float = 0f,
val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS,
val batteryLevel: Int = 0, val batteryLevel: Int = 0,
) {
fun displayTemperature(): String {
return when (temperatureUnit) {
TemperatureUnit.CELSIUS -> String.format("%.1f °C", temperatureValue)
TemperatureUnit.FAHRENHEIT -> String.format("%.1f °F", temperatureValue * 1.8f + 32f)
TemperatureUnit.KELVIN -> String.format("%.1f °K", temperatureValue + 273.15f)
}
}
fun getTemperatureUnit(label: String): TemperatureUnit {
return when (label) {
DISPLAY_CELSIUS -> TemperatureUnit.CELSIUS
DISPLAY_FAHRENHEIT -> TemperatureUnit.FAHRENHEIT
DISPLAY_KELVIN -> TemperatureUnit.KELVIN
else -> throw IllegalArgumentException("Can't create TemperatureUnit from this label: $label")
}
}
fun temperatureSettingsItems(): RadioGroupViewEntity {
return RadioGroupViewEntity(
TemperatureUnit.values().map { createRadioButtonItem(it) }
) )
}
private fun createRadioButtonItem(unit: TemperatureUnit): RadioButtonItem {
return RadioButtonItem(displayTemperature(unit), unit == temperatureUnit)
}
private fun displayTemperature(unit: TemperatureUnit): String {
return when (unit) {
TemperatureUnit.CELSIUS -> DISPLAY_CELSIUS
TemperatureUnit.FAHRENHEIT -> DISPLAY_FAHRENHEIT
TemperatureUnit.KELVIN -> DISPLAY_KELVIN
}
}
}
internal enum class TemperatureUnit {
CELSIUS,
FAHRENHEIT,
KELVIN
}

View File

@@ -1,49 +1,70 @@
package no.nordicsemi.android.hts.data package no.nordicsemi.android.hts.data
import kotlinx.coroutines.channels.BufferOverflow import android.bluetooth.BluetoothDevice
import kotlinx.coroutines.flow.* import android.content.Context
import no.nordicsemi.android.service.BleManagerStatus import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.hts.repository.HTSManager
import no.nordicsemi.android.hts.repository.HTSService
import no.nordicsemi.android.service.BleManagerResult
import no.nordicsemi.android.service.ConnectingResult
import no.nordicsemi.android.service.ServiceManager
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class HTSRepository @Inject constructor() { class HTSRepository @Inject constructor(
@ApplicationContext
private val context: Context,
private val serviceManager: ServiceManager,
) {
private var manager: HTSManager? = null
private val _data = MutableStateFlow(HTSData()) private val _data = MutableStateFlow<BleManagerResult<HTSData>>(ConnectingResult())
val data: StateFlow<HTSData> = _data internal val data = _data.asStateFlow()
private val _command = MutableSharedFlow<DisconnectCommand>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_LATEST) private val _isRunning = MutableStateFlow(false)
val command = _command.asSharedFlow() val isRunning = _isRunning.asStateFlow()
private val _status = MutableStateFlow(BleManagerStatus.CONNECTING) fun launch(device: BluetoothDevice) {
val status = _status.asStateFlow() serviceManager.startService(HTSService::class.java, device)
fun setNewTemperature(temperature: Float) {
_data.tryEmit(_data.value.copy(temperatureValue = temperature))
} }
fun setBatteryLevel(batteryLevel: Int) { fun start(device: BluetoothDevice, scope: CoroutineScope) {
_data.tryEmit(_data.value.copy(batteryLevel = batteryLevel)) val manager = HTSManager(context, scope)
} this.manager = manager
fun setTemperatureUnit(unit: TemperatureUnit) { manager.dataHolder.status.onEach {
_data.tryEmit(_data.value.copy(temperatureUnit = unit)) _data.value = it
} }.launchIn(scope)
fun sendDisconnectCommand() { scope.launch {
if (_command.subscriptionCount.value > 0) { manager.start(device)
_command.tryEmit(DisconnectCommand)
} else {
_status.tryEmit(BleManagerStatus.DISCONNECTED)
} }
} }
fun setNewStatus(status: BleManagerStatus) { private suspend fun HTSManager.start(device: BluetoothDevice) {
_status.value = status try {
connect(device)
.useAutoConnect(false)
.retry(3, 100)
.suspend()
_isRunning.value = true
} catch (e: Exception) {
e.printStackTrace()
}
} }
fun clear() { fun release() {
_status.value = BleManagerStatus.CONNECTING serviceManager.stopService(HTSService::class.java)
_data.tryEmit(HTSData()) manager?.disconnect()?.enqueue()
manager = null
_isRunning.value = false
} }
} }

View File

@@ -24,70 +24,77 @@ package no.nordicsemi.android.hts.repository
import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.content.Context import android.content.Context
import android.util.Log
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import no.nordicsemi.android.ble.BleManager
import no.nordicsemi.android.ble.common.callback.battery.BatteryLevelResponse
import no.nordicsemi.android.ble.common.callback.ht.TemperatureMeasurementResponse import no.nordicsemi.android.ble.common.callback.ht.TemperatureMeasurementResponse
import no.nordicsemi.android.ble.ktx.asValidResponseFlow import no.nordicsemi.android.ble.ktx.asValidResponseFlow
import no.nordicsemi.android.ble.ktx.suspend import no.nordicsemi.android.hts.data.HTSData
import no.nordicsemi.android.hts.data.HTSRepository import no.nordicsemi.android.service.ConnectionObserverAdapter
import no.nordicsemi.android.service.BatteryManager
import java.util.* import java.util.*
val HTS_SERVICE_UUID: UUID = UUID.fromString("00001809-0000-1000-8000-00805f9b34fb") val HTS_SERVICE_UUID: UUID = UUID.fromString("00001809-0000-1000-8000-00805f9b34fb")
private val HT_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A1C-0000-1000-8000-00805f9b34fb") private val HT_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A1C-0000-1000-8000-00805f9b34fb")
private val BATTERY_SERVICE_UUID = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb")
private val BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb")
internal class HTSManager internal constructor( internal class HTSManager internal constructor(
context: Context, context: Context,
scope: CoroutineScope, private val scope: CoroutineScope,
private val dataHolder: HTSRepository ) : BleManager(context) {
) : BatteryManager(context, scope) {
private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null
private var htCharacteristic: BluetoothGattCharacteristic? = null private var htCharacteristic: BluetoothGattCharacteristic? = null
private val exceptionHandler = CoroutineExceptionHandler { _, t-> private val data = MutableStateFlow(HTSData())
Log.e("COROUTINE-EXCEPTION", "Uncaught exception", t) val dataHolder = ConnectionObserverAdapter<HTSData>()
init {
setConnectionObserver(dataHolder)
data.onEach {
dataHolder.setValue(it)
}.launchIn(scope)
} }
override fun onBatteryLevelChanged(batteryLevel: Int) { override fun getGattCallback(): BleManagerGattCallback {
dataHolder.setBatteryLevel(batteryLevel)
}
override fun getGattCallback(): BatteryManagerGattCallback {
return HTManagerGattCallback() return HTManagerGattCallback()
} }
private inner class HTManagerGattCallback : BatteryManagerGattCallback() { private inner class HTManagerGattCallback : BleManagerGattCallback() {
override fun initialize() { override fun initialize() {
super.initialize() super.initialize()
setIndicationCallback(htCharacteristic) setIndicationCallback(htCharacteristic)
.asValidResponseFlow<TemperatureMeasurementResponse>() .asValidResponseFlow<TemperatureMeasurementResponse>()
.onEach { .onEach {
dataHolder.setNewTemperature(it.temperature) data.tryEmit(data.value.copy(temperatureValue = it.temperature))
}.launchIn(scope) }.launchIn(scope)
enableIndications(htCharacteristic).enqueue()
scope.launch(exceptionHandler) { setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow<BatteryLevelResponse>().onEach {
enableIndications(htCharacteristic).suspend() data.value = data.value.copy(batteryLevel = it.batteryLevel)
} }.launchIn(scope)
enableNotifications(batteryLevelCharacteristic).enqueue()
} }
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
val service = gatt.getService(HTS_SERVICE_UUID) gatt.getService(HTS_SERVICE_UUID)?.run {
if (service != null) { htCharacteristic = getCharacteristic(HT_MEASUREMENT_CHARACTERISTIC_UUID)
htCharacteristic = service.getCharacteristic(HT_MEASUREMENT_CHARACTERISTIC_UUID) }
gatt.getService(BATTERY_SERVICE_UUID)?.run {
batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)
} }
return htCharacteristic != null return htCharacteristic != null
} }
override fun onDeviceDisconnected() { override fun onServicesInvalidated() {
super.onDeviceDisconnected()
htCharacteristic = null htCharacteristic = null
} batteryLevelCharacteristic = null
}
override fun onServicesInvalidated() {}
} }
} }

View File

@@ -1,25 +1,27 @@
package no.nordicsemi.android.hts.repository package no.nordicsemi.android.hts.repository
import android.bluetooth.BluetoothDevice
import android.content.Intent
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.hts.data.HTSRepository import no.nordicsemi.android.hts.data.HTSRepository
import no.nordicsemi.android.service.ForegroundBleService import no.nordicsemi.android.service.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
internal class HTSService : ForegroundBleService() { internal class HTSService : NotificationService() {
@Inject @Inject
lateinit var repository: HTSRepository lateinit var repository: HTSRepository
override val manager: HTSManager by lazy { HTSManager(this, scope, repository) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
override fun onCreate() { val device = intent!!.getParcelableExtra<BluetoothDevice>(DEVICE_DATA)!!
super.onCreate()
repository.command.onEach { repository.start(device, lifecycleScope)
stopSelf()
}.launchIn(scope) return START_REDELIVER_INTENT
} }
} }

View File

@@ -18,7 +18,7 @@ import no.nordicsemi.android.theme.view.ScreenSection
import no.nordicsemi.android.theme.view.SectionTitle import no.nordicsemi.android.theme.view.SectionTitle
@Composable @Composable
internal fun HTSContentView(state: HTSData, onEvent: (HTSScreenViewEvent) -> Unit) { internal fun HTSContentView(state: HTSData, temperatureUnit: TemperatureUnit, onEvent: (HTSScreenViewEvent) -> Unit) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -30,21 +30,21 @@ internal fun HTSContentView(state: HTSData, onEvent: (HTSScreenViewEvent) -> Uni
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
RadioButtonGroup(viewEntity = state.temperatureSettingsItems()) { RadioButtonGroup(viewEntity = temperatureUnit.temperatureSettingsItems()) {
onEvent(OnTemperatureUnitSelected(state.getTemperatureUnit(it.label))) onEvent(OnTemperatureUnitSelected(it.label.toTemperatureUnit()))
} }
} }
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
ScreenSection { ScreenSection {
SectionTitle(resId = R.drawable.ic_records, title = "Records") SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.hts_records_section))
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
KeyValueField( KeyValueField(
stringResource(id = R.string.hts_temperature), stringResource(id = R.string.hts_temperature),
state.displayTemperature() displayTemperature(state.temperatureValue, temperatureUnit)
) )
} }
@@ -65,5 +65,5 @@ internal fun HTSContentView(state: HTSData, onEvent: (HTSScreenViewEvent) -> Uni
@Preview @Preview
@Composable @Composable
private fun Preview() { private fun Preview() {
HTSContentView(state = HTSData()) { } HTSContentView(state = HTSData(), TemperatureUnit.CELSIUS) { }
} }

View File

@@ -0,0 +1,43 @@
package no.nordicsemi.android.hts.view
import no.nordicsemi.android.material.you.RadioButtonItem
import no.nordicsemi.android.material.you.RadioGroupViewEntity
private const val DISPLAY_FAHRENHEIT = "°F"
private const val DISPLAY_CELSIUS = "°C"
private const val DISPLAY_KELVIN = "°K"
internal fun displayTemperature(value: Float, temperatureUnit: TemperatureUnit): String {
return when (temperatureUnit) {
TemperatureUnit.CELSIUS -> String.format("%.1f °C", value)
TemperatureUnit.FAHRENHEIT -> String.format("%.1f °F", value * 1.8f + 32f)
TemperatureUnit.KELVIN -> String.format("%.1f °K", value + 273.15f)
}
}
internal fun String.toTemperatureUnit(): TemperatureUnit {
return when (this) {
DISPLAY_CELSIUS -> TemperatureUnit.CELSIUS
DISPLAY_FAHRENHEIT -> TemperatureUnit.FAHRENHEIT
DISPLAY_KELVIN -> TemperatureUnit.KELVIN
else -> throw IllegalArgumentException("Can't create TemperatureUnit from this label: $this")
}
}
internal fun TemperatureUnit.temperatureSettingsItems(): RadioGroupViewEntity {
return RadioGroupViewEntity(
TemperatureUnit.values().map { createRadioButtonItem(it, this) }
)
}
private fun createRadioButtonItem(unit: TemperatureUnit, selectedTemperatureUnit: TemperatureUnit): RadioButtonItem {
return RadioButtonItem(displayTemperature(unit), unit == selectedTemperatureUnit)
}
private fun displayTemperature(unit: TemperatureUnit): String {
return when (unit) {
TemperatureUnit.CELSIUS -> DISPLAY_CELSIUS
TemperatureUnit.FAHRENHEIT -> DISPLAY_FAHRENHEIT
TemperatureUnit.KELVIN -> DISPLAY_KELVIN
}
}

View File

@@ -10,7 +10,13 @@ import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import no.nordicsemi.android.hts.R import no.nordicsemi.android.hts.R
import no.nordicsemi.android.hts.viewmodel.HTSViewModel import no.nordicsemi.android.hts.viewmodel.HTSViewModel
import no.nordicsemi.android.service.*
import no.nordicsemi.android.theme.view.BackIconAppBar import no.nordicsemi.android.theme.view.BackIconAppBar
import no.nordicsemi.android.theme.view.scanner.DeviceConnectingView
import no.nordicsemi.android.theme.view.scanner.DeviceDisconnectedView
import no.nordicsemi.android.theme.view.scanner.NoDeviceView
import no.nordicsemi.android.theme.view.scanner.Reason
import no.nordicsemi.android.utils.exhaustive
@Composable @Composable
fun HTSScreen() { fun HTSScreen() {
@@ -18,15 +24,22 @@ fun HTSScreen() {
val state = viewModel.state.collectAsState().value val state = viewModel.state.collectAsState().value
Column { Column {
BackIconAppBar(stringResource(id = R.string.hts_title)) { val navigateUp = { viewModel.onEvent(NavigateUp) }
viewModel.onEvent(DisconnectEvent)
} BackIconAppBar(stringResource(id = R.string.hts_title), navigateUp)
Column(modifier = Modifier.verticalScroll(rememberScrollState())) { Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
// when (state) { when (state.htsManagerState) {
// is DisplayDataState -> HTSContentView(state.data) { viewModel.onEvent(it) } NoDeviceState -> NoDeviceView()
// LoadingState -> DeviceConnectingView() is WorkingState -> when (state.htsManagerState.result) {
// }.exhaustive is ConnectingResult,
is ReadyResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
is SuccessResult -> HTSContentView(state.htsManagerState.result.data, state.temperatureUnit) { viewModel.onEvent(it) }
}
}.exhaustive
} }
} }
} }

View File

@@ -1,9 +1,9 @@
package no.nordicsemi.android.hts.view package no.nordicsemi.android.hts.view
import no.nordicsemi.android.hts.data.TemperatureUnit
internal sealed class HTSScreenViewEvent internal sealed class HTSScreenViewEvent
internal data class OnTemperatureUnitSelected(val value: TemperatureUnit) : HTSScreenViewEvent() internal data class OnTemperatureUnitSelected(val value: TemperatureUnit) : HTSScreenViewEvent()
internal object DisconnectEvent : HTSScreenViewEvent() internal object DisconnectEvent : HTSScreenViewEvent()
internal object NavigateUp : HTSScreenViewEvent()

View File

@@ -1,9 +1,15 @@
package no.nordicsemi.android.hts.view package no.nordicsemi.android.hts.view
import no.nordicsemi.android.hts.data.HTSData import no.nordicsemi.android.hts.data.HTSData
import no.nordicsemi.android.service.BleManagerResult
internal sealed class HTSViewState internal data class HTSViewState(
val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS,
val htsManagerState: HTSManagerState = NoDeviceState
)
internal object LoadingState : HTSViewState() internal sealed class HTSManagerState
internal data class DisplayDataState(val data: HTSData) : HTSViewState() internal data class WorkingState(val result: BleManagerResult<HTSData>) : HTSManagerState()
internal object NoDeviceState : HTSManagerState()

View File

@@ -0,0 +1,7 @@
package no.nordicsemi.android.hts.view
internal enum class TemperatureUnit {
CELSIUS,
FAHRENHEIT,
KELVIN
}

View File

@@ -9,7 +9,6 @@ import no.nordicsemi.android.hts.repository.HTSService
import no.nordicsemi.android.hts.repository.HTS_SERVICE_UUID import no.nordicsemi.android.hts.repository.HTS_SERVICE_UUID
import no.nordicsemi.android.hts.view.* import no.nordicsemi.android.hts.view.*
import no.nordicsemi.android.navigation.* import no.nordicsemi.android.navigation.*
import no.nordicsemi.android.service.BleManagerStatus
import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.service.ServiceManager
import no.nordicsemi.android.utils.exhaustive import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.android.utils.getDevice import no.nordicsemi.android.utils.getDevice
@@ -23,15 +22,20 @@ internal class HTSViewModel @Inject constructor(
private val navigationManager: NavigationManager private val navigationManager: NavigationManager
) : ViewModel() { ) : ViewModel() {
val state = repository.data.combine(repository.status) { data, status -> private val _state = MutableStateFlow(HTSViewState())
// when (status) { val state = _state.asStateFlow()
// BleManagerStatus.CONNECTING -> LoadingState
// BleManagerStatus.OK,
// BleManagerStatus.DISCONNECTED -> DisplayDataState(data)
// }
}.stateIn(viewModelScope, SharingStarted.Lazily, LoadingState)
init { init {
if (!repository.isRunning.value) {
requestBluetoothDevice()
}
repository.data.onEach {
_state.value = _state.value.copy(htsManagerState = WorkingState(it))
}.launchIn(viewModelScope)
}
private fun requestBluetoothDevice() {
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(HTS_SERVICE_UUID)) navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(HTS_SERVICE_UUID))
navigationManager.recentResult.onEach { navigationManager.recentResult.onEach {
@@ -39,39 +43,29 @@ internal class HTSViewModel @Inject constructor(
handleArgs(it) handleArgs(it)
} }
}.launchIn(viewModelScope) }.launchIn(viewModelScope)
repository.status.onEach {
if (it == BleManagerStatus.DISCONNECTED) {
navigationManager.navigateUp()
}
}.launchIn(viewModelScope)
} }
private fun handleArgs(args: DestinationResult) { private fun handleArgs(args: DestinationResult) {
when (args) { when (args) {
is CancelDestinationResult -> navigationManager.navigateUp() is CancelDestinationResult -> navigationManager.navigateUp()
is SuccessDestinationResult -> serviceManager.startService(HTSService::class.java, args.getDevice()) is SuccessDestinationResult -> repository.launch(args.getDevice().device)
}.exhaustive }.exhaustive
} }
fun onEvent(event: HTSScreenViewEvent) { fun onEvent(event: HTSScreenViewEvent) {
when (event) { when (event) {
DisconnectEvent -> onDisconnectButtonClick() DisconnectEvent -> disconnect()
is OnTemperatureUnitSelected -> onTemperatureUnitSelected(event) is OnTemperatureUnitSelected -> onTemperatureUnitSelected(event)
NavigateUp -> navigationManager.navigateUp()
}.exhaustive }.exhaustive
} }
private fun onDisconnectButtonClick() { private fun disconnect() {
repository.sendDisconnectCommand() repository.release()
repository.clear() navigationManager.navigateUp()
} }
private fun onTemperatureUnitSelected(event: OnTemperatureUnitSelected) { private fun onTemperatureUnitSelected(event: OnTemperatureUnitSelected) {
repository.setTemperatureUnit(event.value) _state.value = _state.value.copy(temperatureUnit = event.value)
}
override fun onCleared() {
super.onCleared()
repository.clear()
} }
} }

View File

@@ -7,4 +7,5 @@
<string name="hts_kelvin">%.1f °K</string> <string name="hts_kelvin">%.1f °K</string>
<string name="hts_temperature">Temperature</string> <string name="hts_temperature">Temperature</string>
<string name="hts_records_section">Records</string>
</resources> </resources>