Change PRX module

This commit is contained in:
Sylwester Zieliński
2022-02-14 15:24:28 +01:00
parent 382208454f
commit f92e1b4adb
15 changed files with 277 additions and 196 deletions

View File

@@ -1,6 +1,6 @@
#Wed Sep 08 10:36:20 CEST 2021
#Mon Feb 14 14:46:55 CET 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip

View File

@@ -6,4 +6,5 @@ import no.nordicsemi.android.service.BleManagerResult
internal sealed class HRSViewState
internal data class WorkingState(val result: BleManagerResult<HRSData>) : HRSViewState()
internal object NoDeviceState : HRSViewState()

View File

@@ -89,7 +89,7 @@ internal class HTSManager internal constructor(
gatt.getService(BATTERY_SERVICE_UUID)?.run {
batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)
}
return htCharacteristic != null
return htCharacteristic != null && batteryLevelCharacteristic != null
}
override fun onServicesInvalidated() {

View File

@@ -3,9 +3,11 @@ package no.nordicsemi.android.hts.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.hts.data.HTSRepository
import no.nordicsemi.android.hts.repository.HTSService
import no.nordicsemi.android.hts.repository.HTS_SERVICE_UUID
import no.nordicsemi.android.hts.view.*
import no.nordicsemi.android.navigation.*

View File

@@ -13,6 +13,7 @@ dependencies {
implementation libs.nordic.ui.scanner
implementation libs.nordic.navigation
implementation libs.bundles.icons
implementation libs.bundles.compose
implementation libs.androidx.core
implementation libs.material

View File

@@ -1,62 +1,97 @@
package no.nordicsemi.android.prx.data
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.*
import no.nordicsemi.android.service.BleManagerStatus
import android.bluetooth.BluetoothDevice
import android.content.Context
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.prx.repository.AlarmHandler
import no.nordicsemi.android.prx.repository.PRXManager
import no.nordicsemi.android.prx.repository.PRXService
import no.nordicsemi.android.prx.repository.ProximityServerManager
import no.nordicsemi.android.service.BleManagerResult
import no.nordicsemi.android.service.ConnectingResult
import no.nordicsemi.android.service.ServiceManager
import no.nordicsemi.android.service.SuccessResult
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class PRXRepository @Inject constructor() {
internal class PRXRepository @Inject constructor(
@ApplicationContext
private val context: Context,
private val serviceManager: ServiceManager,
private val proximityServerManager: ProximityServerManager,
private val alarmHandler: AlarmHandler
) {
private val _data = MutableStateFlow(PRXData())
val data: StateFlow<PRXData> = _data
private var manager: PRXManager? = null
private val _command = MutableSharedFlow<PRXCommand>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
val command = _command.asSharedFlow()
private val _data = MutableStateFlow<BleManagerResult<PRXData>>(ConnectingResult())
internal val data = _data.asStateFlow()
private val _status = MutableStateFlow(BleManagerStatus.CONNECTING)
val status = _status.asStateFlow()
private val _isRunning = MutableStateFlow(false)
val isRunning = _isRunning.asStateFlow()
fun setBatteryLevel(batteryLevel: Int) {
_data.tryEmit(_data.value.copy(batteryLevel = batteryLevel))
fun launch(device: BluetoothDevice) {
serviceManager.startService(PRXService::class.java, device)
proximityServerManager.open()
}
fun setLocalAlarmLevel(value: Int) {
val alarmLevel = AlarmLevel.create(value)
_data.tryEmit(_data.value.copy(localAlarmLevel = alarmLevel))
}
fun start(device: BluetoothDevice, scope: CoroutineScope) {
val manager = PRXManager(context, scope)
this.manager = manager
manager.useServer(proximityServerManager)
fun setLocalAlarmLevel(alarmLevel: AlarmLevel) {
_data.tryEmit(_data.value.copy(localAlarmLevel = alarmLevel))
}
manager.dataHolder.status.onEach {
_data.value = it
handleLocalAlarm(it)
}.launchIn(scope)
fun setLinkLossLevel(value: Int) {
val alarmLevel = AlarmLevel.create(value)
_data.tryEmit(_data.value.copy(linkLossAlarmLevel = alarmLevel))
}
fun setRemoteAlarmLevel(isOn: Boolean) {
_data.tryEmit(_data.value.copy(isRemoteAlarm = isOn))
}
fun invokeCommand(command: PRXCommand) {
if (command == Disconnect) {
_command.tryEmit(command)
_status.tryEmit(BleManagerStatus.DISCONNECTED)
} else if (_command.subscriptionCount.value > 0) {
_command.tryEmit(command)
} else {
_status.tryEmit(BleManagerStatus.DISCONNECTED)
scope.launch {
manager.start(device)
}
}
fun setNewStatus(status: BleManagerStatus) {
_status.value = status
private suspend fun PRXManager.start(device: BluetoothDevice) {
try {
connect(device)
.useAutoConnect(false)
.retry(3, 100)
.suspend()
_isRunning.value = true
} catch (e: Exception) {
e.printStackTrace()
}
}
fun clear() {
_status.value = BleManagerStatus.CONNECTING
_data.tryEmit(PRXData())
private fun handleLocalAlarm(result: BleManagerResult<PRXData>) {
(result as? SuccessResult<PRXData>)?.let {
if (it.data.localAlarmLevel != AlarmLevel.NONE) {
alarmHandler.playAlarm(it.data.localAlarmLevel)
} else {
alarmHandler.pauseAlarm()
}
}
}
fun enableAlarm() {
manager?.writeImmediateAlert(true)
}
fun disableAlarm() {
manager?.writeImmediateAlert(false)
}
fun release() {
serviceManager.stopService(PRXService::class.java)
manager?.disconnect()?.enqueue()
manager = null
_isRunning.value = false
}
}

View File

@@ -26,65 +26,84 @@ import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattServer
import android.content.Context
import android.util.Log
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.ble.BleManager
import no.nordicsemi.android.ble.common.callback.alert.AlertLevelDataCallback
import no.nordicsemi.android.ble.common.callback.battery.BatteryLevelResponse
import no.nordicsemi.android.ble.common.data.alert.AlertLevelData
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.prx.data.PRXRepository
import no.nordicsemi.android.service.BatteryManager
import no.nordicsemi.android.prx.data.AlarmLevel
import no.nordicsemi.android.prx.data.PRXData
import no.nordicsemi.android.service.ConnectionObserverAdapter
import no.nordicsemi.android.utils.launchWithCatch
import java.util.*
val LINK_LOSS_SERVICE_UUID = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb")
val PRX_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb")
val LINK_LOSS_SERVICE_UUID = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb")
val ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A06-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 PRXManager(
context: Context,
scope: CoroutineScope,
private val dataHolder: PRXRepository
) : BatteryManager(context, scope) {
private val scope: CoroutineScope,
) : BleManager(context) {
private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null
private var alertLevelCharacteristic: BluetoothGattCharacteristic? = null
private var linkLossCharacteristic: BluetoothGattCharacteristic? = null
private var localAlertLevelCharacteristic: BluetoothGattCharacteristic? = null
private var linkLossServerCharacteristic: BluetoothGattCharacteristic? = null
private val exceptionHandler = CoroutineExceptionHandler { _, t->
Log.e("COROUTINE-EXCEPTION", "Uncaught exception", t)
private var isAlertEnabled = false
private val data = MutableStateFlow(PRXData())
val dataHolder = ConnectionObserverAdapter<PRXData>()
init {
setConnectionObserver(dataHolder)
data.onEach {
dataHolder.setValue(it)
}.launchIn(scope)
}
var isAlertEnabled = false
private set
private inner class ProximityManagerGattCallback : BatteryManagerGattCallback() {
private inner class ProximityManagerGattCallback : BleManagerGattCallback() {
override fun initialize() {
super.initialize()
setWriteCallback(localAlertLevelCharacteristic)
.with(object : AlertLevelDataCallback() {
override fun onAlertLevelChanged(device: BluetoothDevice, level: Int) {
dataHolder.setLocalAlarmLevel(level)
data.value = data.value.copy(localAlarmLevel = AlarmLevel.create(level))
}
})
setWriteCallback(linkLossServerCharacteristic)
.with(object : AlertLevelDataCallback() {
override fun onAlertLevelChanged(device: BluetoothDevice, level: Int) {
dataHolder.setLinkLossLevel(level)
data.value = data.value.copy(linkLossAlarmLevel = AlarmLevel.create(level))
}
})
scope.launch(exceptionHandler) {
writeCharacteristic(
linkLossCharacteristic,
AlertLevelData.highAlert(),
BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
).suspend()
}
writeCharacteristic(
linkLossCharacteristic,
AlertLevelData.highAlert(),
BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
).enqueue()
setNotificationCallback(batteryLevelCharacteristic)
.asValidResponseFlow<BatteryLevelResponse>()
.onEach {
data.value = data.value.copy(batteryLevel = it.batteryLevel)
}.launchIn(scope)
enableNotifications(batteryLevelCharacteristic).enqueue()
}
override fun onServerReady(server: BluetoothGattServer) {
@@ -98,29 +117,26 @@ internal class PRXManager(
}
}
override fun onServicesInvalidated() { }
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
val llService = gatt.getService(LINK_LOSS_SERVICE_UUID)
if (llService != null) {
linkLossCharacteristic = llService.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)
gatt.getService(LINK_LOSS_SERVICE_UUID)?.run {
linkLossCharacteristic = getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)
}
gatt.getService(BATTERY_SERVICE_UUID)?.run {
batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)
}
return linkLossCharacteristic != null
}
override fun isOptionalServiceSupported(gatt: BluetoothGatt): Boolean {
super.isOptionalServiceSupported(gatt)
val iaService = gatt.getService(PRX_SERVICE_UUID)
if (iaService != null) {
alertLevelCharacteristic = iaService.getCharacteristic(
ALERT_LEVEL_CHARACTERISTIC_UUID
)
gatt.getService(PRX_SERVICE_UUID)?.run {
alertLevelCharacteristic = getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)
}
return alertLevelCharacteristic != null
return alertLevelCharacteristic != null && batteryLevelCharacteristic != null
}
override fun onDeviceDisconnected() {
super.onDeviceDisconnected()
override fun onServicesInvalidated() {
batteryLevelCharacteristic = null
alertLevelCharacteristic = null
linkLossCharacteristic = null
localAlertLevelCharacteristic = null
@@ -131,7 +147,7 @@ internal class PRXManager(
fun writeImmediateAlert(on: Boolean) {
if (!isConnected) return
scope.launch(exceptionHandler) {
scope.launchWithCatch {
writeCharacteristic(
alertLevelCharacteristic,
if (on) AlertLevelData.highAlert() else AlertLevelData.noAlert(),
@@ -139,14 +155,10 @@ internal class PRXManager(
).suspend()
isAlertEnabled = on
dataHolder.setRemoteAlarmLevel(on)
data.value = data.value.copy(isRemoteAlarm = on)
}
}
override fun onBatteryLevelChanged(batteryLevel: Int) {
dataHolder.setBatteryLevel(batteryLevel)
}
override fun getGattCallback(): BleManagerGattCallback {
return ProximityManagerGattCallback()
}

View File

@@ -1,77 +1,27 @@
package no.nordicsemi.android.prx.repository
import android.bluetooth.BluetoothDevice
import android.content.Intent
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.prx.data.*
import no.nordicsemi.android.service.ForegroundBleService
import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.android.prx.data.PRXRepository
import no.nordicsemi.android.service.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService
import javax.inject.Inject
@AndroidEntryPoint
internal class PRXService : ForegroundBleService() {
internal class PRXService : NotificationService() {
@Inject
lateinit var repository: PRXRepository
@Inject
lateinit var alarmHandler: AlarmHandler
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
private var serverManager: ProximityServerManager = ProximityServerManager(this)
val device = intent!!.getParcelableExtra<BluetoothDevice>(DEVICE_DATA)!!
override val manager: PRXManager by lazy {
PRXManager(this, scope, repository).apply {
useServer(serverManager)
}
}
repository.start(device, lifecycleScope)
override fun onCreate() {
super.onCreate()
serverManager.open()
// status.onEach {
// val bleStatus = when (it) {
// BleServiceStatus.CONNECTING -> BleManagerStatus.CONNECTING
// BleServiceStatus.OK -> BleManagerStatus.OK
// BleServiceStatus.DISCONNECTED -> {
// scope.close()
// stopSelf()
// BleManagerStatus.DISCONNECTED
// }
// BleServiceStatus.LINK_LOSS -> null
// }.exhaustive
// bleStatus?.let { repository.setNewStatus(it) }
//
// if (BleServiceStatus.LINK_LOSS == it) {
// repository.setLocalAlarmLevel(repository.data.value.linkLossAlarmLevel)
// }
// }.launchIn(scope)
repository.command.onEach {
when (it) {
DisableAlarm -> manager.writeImmediateAlert(false)
EnableAlarm -> manager.writeImmediateAlert(true)
Disconnect -> stopSelf()
}.exhaustive
}.launchIn(scope)
repository.data.onEach {
if (it.localAlarmLevel != AlarmLevel.NONE) {
alarmHandler.playAlarm(it.localAlarmLevel)
} else {
alarmHandler.pauseAlarm()
}
}.launchIn(scope)
}
override fun shouldAutoConnect(): Boolean {
return true
}
override fun onDestroy() {
super.onDestroy()
alarmHandler.releaseAlarm()
serverManager.close()
return START_REDELIVER_INTENT
}
}

View File

@@ -25,11 +25,16 @@ import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattService
import android.content.Context
import android.util.Log
import dagger.hilt.android.qualifiers.ApplicationContext
import no.nordicsemi.android.ble.BleServerManager
import no.nordicsemi.android.ble.common.data.alert.AlertLevelData
import java.util.*
import javax.inject.Inject
internal class ProximityServerManager @Inject constructor(
@ApplicationContext
context: Context
) : BleServerManager(context) {
internal class ProximityServerManager(context: Context) : BleServerManager(context) {
override fun log(priority: Int, message: String) {
Log.println(priority, "BleManager", message)
}
@@ -59,4 +64,4 @@ internal class ProximityServerManager(context: Context) : BleServerManager(conte
)
return services
}
}
}

View File

@@ -0,0 +1,65 @@
package no.nordicsemi.android.prx.view
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import no.nordicsemi.android.prx.R
import no.nordicsemi.android.theme.R as themeR
import no.nordicsemi.android.theme.view.ScreenSection
import androidx.compose.material.icons.filled.HighlightOff
@Composable
fun DeviceOutOfRangeView(navigateUp: () -> Unit) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
ScreenSection {
Icon(
imageVector = Icons.Default.HighlightOff,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSecondary,
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.secondary,
shape = CircleShape
)
.padding(8.dp)
)
Spacer(modifier = Modifier.size(16.dp))
Text(
text = stringResource(id = R.string.prx_device_out_of_range),
style = MaterialTheme.typography.titleMedium
)
Spacer(modifier = Modifier.size(16.dp))
Text(
text = stringResource(id = R.string.prx_device_out_of_range_reason),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium
)
}
Spacer(modifier = Modifier.size(16.dp))
Button(onClick = { navigateUp() }) {
Text(text = stringResource(id = themeR.string.disconnect))
}
}
}

View File

@@ -11,7 +11,13 @@ import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import no.nordicsemi.android.prx.R
import no.nordicsemi.android.prx.viewmodel.PRXViewModel
import no.nordicsemi.android.service.*
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
fun PRXScreen() {
@@ -19,15 +25,22 @@ fun PRXScreen() {
val state = viewModel.state.collectAsState().value
Column(horizontalAlignment = Alignment.CenterHorizontally) {
BackIconAppBar(stringResource(id = R.string.prx_title)) {
viewModel.onEvent(DisconnectEvent)
}
val navigateUp = { viewModel.onEvent(NavigateUpEvent) }
BackIconAppBar(stringResource(id = R.string.prx_title), navigateUp)
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
// when (state) {
// is DisplayDataState -> ContentView(state.data) { viewModel.onEvent(it) }
// LoadingState -> DeviceConnectingView()
// }.exhaustive
when (state) {
NoDeviceState -> NoDeviceView()
is WorkingState -> when (state.result) {
is ConnectingResult,
is ReadyResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
is LinkLossResult -> DeviceOutOfRangeView { viewModel.onEvent(DisconnectEvent) }
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
is SuccessResult -> ContentView(state.result.data) { viewModel.onEvent(it) }
}
}.exhaustive
}
}
}

View File

@@ -2,6 +2,8 @@ package no.nordicsemi.android.prx.view
internal sealed class PRXScreenViewEvent
internal object NavigateUpEvent : PRXScreenViewEvent()
internal object TurnOnAlert : PRXScreenViewEvent()
internal object TurnOffAlert : PRXScreenViewEvent()

View File

@@ -1,9 +1,10 @@
package no.nordicsemi.android.prx.view
import no.nordicsemi.android.prx.data.PRXData
import no.nordicsemi.android.service.BleManagerResult
internal sealed class PRXViewState
internal object LoadingState : PRXViewState()
internal data class WorkingState(val result: BleManagerResult<PRXData>) : PRXViewState()
internal data class DisplayDataState(val data: PRXData) : PRXViewState()
internal object NoDeviceState : PRXViewState()

View File

@@ -3,17 +3,14 @@ package no.nordicsemi.android.prx.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.navigation.*
import no.nordicsemi.android.prx.data.DisableAlarm
import no.nordicsemi.android.prx.data.Disconnect
import no.nordicsemi.android.prx.data.EnableAlarm
import no.nordicsemi.android.prx.data.PRXRepository
import no.nordicsemi.android.prx.repository.PRXService
import no.nordicsemi.android.prx.repository.PRX_SERVICE_UUID
import no.nordicsemi.android.prx.view.*
import no.nordicsemi.android.service.BleManagerStatus
import no.nordicsemi.android.service.ServiceManager
import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.android.utils.getDevice
import no.nordicsemi.ui.scanner.ScannerDestinationId
@@ -22,19 +19,23 @@ import javax.inject.Inject
@HiltViewModel
internal class PRXViewModel @Inject constructor(
private val repository: PRXRepository,
private val serviceManager: ServiceManager,
private val navigationManager: NavigationManager
) : ViewModel() {
val state = repository.data.combine(repository.status) { data, status ->
// when (status) {
// BleManagerStatus.CONNECTING -> LoadingState
// BleManagerStatus.OK,
// BleManagerStatus.DISCONNECTED -> DisplayDataState(data)
// }
}.stateIn(viewModelScope, SharingStarted.Lazily, LoadingState)
private val _state = MutableStateFlow<PRXViewState>(NoDeviceState)
val state = _state.asStateFlow()
init {
if (!repository.isRunning.value) {
requestBluetoothDevice()
}
repository.data.onEach {
_state.value = WorkingState(it)
}.launchIn(viewModelScope)
}
private fun requestBluetoothDevice() {
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(PRX_SERVICE_UUID))
navigationManager.recentResult.onEach {
@@ -42,36 +43,26 @@ internal class PRXViewModel @Inject constructor(
handleArgs(it)
}
}.launchIn(viewModelScope)
repository.status.onEach {
if (it == BleManagerStatus.DISCONNECTED) {
navigationManager.navigateUp()
}
}.launchIn(viewModelScope)
}
private fun handleArgs(args: DestinationResult) {
when (args) {
is CancelDestinationResult -> navigationManager.navigateUp()
is SuccessDestinationResult -> serviceManager.startService(PRXService::class.java, args.getDevice())
is SuccessDestinationResult -> repository.launch(args.getDevice().device)
}.exhaustive
}
fun onEvent(event: PRXScreenViewEvent) {
when (event) {
DisconnectEvent -> onDisconnectButtonClick()
TurnOffAlert -> repository.invokeCommand(DisableAlarm)
TurnOnAlert -> repository.invokeCommand(EnableAlarm)
DisconnectEvent -> disconnect()
TurnOffAlert -> repository.disableAlarm()
TurnOnAlert -> repository.enableAlarm()
NavigateUpEvent -> navigationManager.navigateUp()
}.exhaustive
}
private fun onDisconnectButtonClick() {
repository.invokeCommand(Disconnect)
repository.clear()
}
override fun onCleared() {
super.onCleared()
repository.clear()
private fun disconnect() {
repository.release()
navigationManager.navigateUp()
}
}

View File

@@ -14,4 +14,7 @@
<string name="prx_is_remote_alarm">Remote alarm</string>
<string name="prx_local_alarm_level">Local alarm level</string>
<string name="prx_device_out_of_range">Device out of range</string>
<string name="prx_device_out_of_range_reason">Device is out of range and it has disconnected. It should reconnect automatically after device is in range again.</string>
</resources>