Fix manager in progress indicator

This commit is contained in:
Sylwester Zieliński
2022-02-15 09:46:45 +01:00
parent a6f0f58448
commit 1ae259fc65
44 changed files with 195 additions and 224 deletions

View File

@@ -4,10 +4,7 @@ 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.flow.*
import kotlinx.coroutines.launch
import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.cgms.repository.CGMManager
@@ -30,8 +27,8 @@ class CGMRepository @Inject constructor(
private val _data = MutableStateFlow<BleManagerResult<CGMData>>(ConnectingResult())
internal val data = _data.asStateFlow()
private val _isRunning = MutableStateFlow(false)
val isRunning = _isRunning.asStateFlow()
val isRunning = data.map { it.isRunning() }
val hasBeenDisconnected = data.map { it.hasBeenDisconnected() }
fun launch(device: BluetoothDevice) {
serviceManager.startService(CGMService::class.java, device)
@@ -56,7 +53,6 @@ class CGMRepository @Inject constructor(
.useAutoConnect(false)
.retry(3, 100)
.suspend()
_isRunning.value = true
} catch (e: Exception) {
e.printStackTrace()
}
@@ -75,9 +71,7 @@ class CGMRepository @Inject constructor(
}
fun release() {
serviceManager.stopService(CGMService::class.java)
manager?.disconnect()?.enqueue()
manager = null
_isRunning.value = false
}
}

View File

@@ -26,7 +26,7 @@ import android.bluetooth.BluetoothGattCharacteristic
import android.content.Context
import android.util.Log
import android.util.SparseArray
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -48,6 +48,7 @@ import no.nordicsemi.android.cgms.data.CGMData
import no.nordicsemi.android.cgms.data.CGMRecord
import no.nordicsemi.android.cgms.data.RequestStatus
import no.nordicsemi.android.service.ConnectionObserverAdapter
import no.nordicsemi.android.utils.launchWithCatch
import java.util.*
val CGMS_SERVICE_UUID: UUID = UUID.fromString("0000181F-0000-1000-8000-00805f9b34fb")
@@ -80,10 +81,6 @@ internal class CGMManager(
private var sessionStartTime: Long = 0
private val exceptionHandler = CoroutineExceptionHandler { _, t ->
Log.e("COROUTINE-EXCEPTION", "Uncaught exception", t)
}
private val data = MutableStateFlow(CGMData())
val dataHolder = ConnectionObserverAdapter<CGMData>()
@@ -101,32 +98,17 @@ internal class CGMManager(
override fun log(priority: Int, message: String) {
super.log(priority, message)
Log.d("COROUTINE-EXCEPTION", message)
Log.d("CGM-PROFILE", message)
}
private inner class CGMManagerGattCallback : BleManagerGattCallback() {
override fun initialize() {
super.initialize()
scope.launch(exceptionHandler) {
val response =
readCharacteristic(cgmFeatureCharacteristic).suspendForValidResponse<CGMFeatureResponse>()
this@CGMManager.secured = response.features.e2eCrcSupported
}
scope.launch(exceptionHandler) {
val response =
readCharacteristic(cgmFeatureCharacteristic).suspendForValidResponse<CGMFeatureResponse>()
this@CGMManager.secured = response.features.e2eCrcSupported
}
scope.launch(exceptionHandler) {
val response =
readCharacteristic(cgmStatusCharacteristic).suspendForValidResponse<CGMStatusResponse>()
if (response.status?.sessionStopped == false) {
sessionStartTime = System.currentTimeMillis() - response.timeOffset * 60000L
}
}
enableNotifications(cgmMeasurementCharacteristic).enqueue()
enableIndications(cgmSpecificOpsControlPointCharacteristic).enqueue()
enableIndications(recordAccessControlPointCharacteristic).enqueue()
enableNotifications(batteryLevelCharacteristic).enqueue()
setNotificationCallback(cgmMeasurementCharacteristic).asValidResponseFlow<ContinuousGlucoseMeasurementResponse>()
.onEach {
@@ -164,7 +146,7 @@ internal class CGMManager(
setIndicationCallback(recordAccessControlPointCharacteristic).asValidResponseFlow<RecordAccessControlPointResponse>()
.onEach {
if (it.isOperationCompleted && !it.wereRecordsFound() && it.numberOfRecords > 0) {
if (it.isOperationCompleted && it.wereRecordsFound() && it.numberOfRecords > 0) {
onRecordsReceived(it)
} else if (it.isOperationCompleted && !it.wereRecordsFound()) {
onNoRecordsFound()
@@ -180,13 +162,16 @@ internal class CGMManager(
data.value = data.value.copy(batteryLevel = it.batteryLevel)
}.launchIn(scope)
enableNotifications(cgmMeasurementCharacteristic).enqueue()
enableIndications(cgmSpecificOpsControlPointCharacteristic).enqueue()
enableIndications(recordAccessControlPointCharacteristic).enqueue()
enableNotifications(batteryLevelCharacteristic).enqueue()
scope.launchWithCatch {
val cgmResponse = readCharacteristic(cgmFeatureCharacteristic).suspendForValidResponse<CGMFeatureResponse>()
this@CGMManager.secured = cgmResponse.features.e2eCrcSupported
if (sessionStartTime == 0L) {
scope.launch(exceptionHandler) {
val response = readCharacteristic(cgmStatusCharacteristic).suspendForValidResponse<CGMStatusResponse>()
if (response.status?.sessionStopped == false) {
sessionStartTime = System.currentTimeMillis() - response.timeOffset * 60000L
}
if (sessionStartTime == 0L) {
writeCharacteristic(
cgmSpecificOpsControlPointCharacteristic,
CGMSpecificOpsControlPointData.startSession(secured),
@@ -282,7 +267,7 @@ internal class CGMManager(
clear()
data.value = data.value.copy(requestStatus = RequestStatus.PENDING)
recordAccessRequestInProgress = true
scope.launch(exceptionHandler) {
scope.launchWithCatch {
writeCharacteristic(
recordAccessControlPointCharacteristic,
RecordAccessControlPointData.reportLastStoredRecord(),
@@ -296,7 +281,7 @@ internal class CGMManager(
clear()
data.value = data.value.copy(requestStatus = RequestStatus.PENDING)
recordAccessRequestInProgress = true
scope.launch(exceptionHandler) {
scope.launchWithCatch {
writeCharacteristic(
recordAccessControlPointCharacteristic,
RecordAccessControlPointData.reportFirstStoredRecord(),
@@ -310,7 +295,7 @@ internal class CGMManager(
clear()
data.value = data.value.copy(requestStatus = RequestStatus.PENDING)
recordAccessRequestInProgress = true
scope.launch(exceptionHandler) {
scope.launchWithCatch {
writeCharacteristic(
recordAccessControlPointCharacteristic,
RecordAccessControlPointData.reportNumberOfAllStoredRecords(),

View File

@@ -4,6 +4,8 @@ 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.cgms.data.CGMRepository
import no.nordicsemi.android.service.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService
@@ -22,6 +24,10 @@ internal class CGMService : NotificationService() {
repository.start(device, lifecycleScope)
repository.hasBeenDisconnected.onEach {
if (it) stopSelf()
}.launchIn(lifecycleScope)
return START_REDELIVER_INTENT
}
}

View File

@@ -32,11 +32,11 @@ fun CGMScreen() {
when (state) {
NoDeviceState -> NoDeviceView()
is WorkingState -> when (state.result) {
is ConnectingResult,
is ReadyResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
is ConnectingResult -> 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 UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
is SuccessResult -> CGMContentView(state.result.data) { viewModel.onEvent(it) }
}
}.exhaustive

View File

@@ -3,10 +3,8 @@ package no.nordicsemi.android.cgms.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import no.nordicsemi.android.cgms.data.CGMRepository
import no.nordicsemi.android.cgms.data.CGMServiceCommand
import no.nordicsemi.android.cgms.repository.CGMS_SERVICE_UUID
@@ -27,8 +25,10 @@ internal class CGMScreenViewModel @Inject constructor(
val state = _state.asStateFlow()
init {
if (!repository.isRunning.value) {
requestBluetoothDevice()
viewModelScope.launch {
if (repository.isRunning.firstOrNull() == false) {
requestBluetoothDevice()
}
}
repository.data.onEach {