diff --git a/lib_utils/src/main/java/no/nordicsemi/android/utils/Ext.kt b/lib_utils/src/main/java/no/nordicsemi/android/utils/Ext.kt index 07901c4a..e241bf63 100644 --- a/lib_utils/src/main/java/no/nordicsemi/android/utils/Ext.kt +++ b/lib_utils/src/main/java/no/nordicsemi/android/utils/Ext.kt @@ -37,6 +37,7 @@ import android.util.Log import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch val String.Companion.EMPTY @@ -53,6 +54,6 @@ private val exceptionHandler = CoroutineExceptionHandler { _, t -> } fun CoroutineScope.launchWithCatch(block: suspend CoroutineScope.() -> Unit) = - launch(Job() + exceptionHandler) { + launch(SupervisorJob() + exceptionHandler) { block() } diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMServiceData.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMServiceData.kt index d3f20a28..5aab70a1 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMServiceData.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMServiceData.kt @@ -8,7 +8,8 @@ internal data class CGMServiceData( val records: List = emptyList(), val batteryLevel: Int? = null, val connectionState: GattConnectionState? = null, - val requestStatus: RequestStatus = RequestStatus.IDLE + val requestStatus: RequestStatus = RequestStatus.IDLE, + val deviceName: String? = null ) data class CGMRecordWithSequenceNumber( diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMRepository.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMRepository.kt index 98117309..19dfb9ae 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMRepository.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMRepository.kt @@ -69,6 +69,7 @@ class CGMRepository @Inject constructor( val highestSequenceNumber = data.value.records.maxOfOrNull { it.sequenceNumber } ?: -1 fun launch(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(CGMService::class.java, device) } diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt index aa308938..c0aa46d9 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt @@ -65,6 +65,7 @@ import no.nordicsemi.android.kotlin.ble.profile.racp.RACPOpCode import no.nordicsemi.android.kotlin.ble.profile.racp.RACPResponseCode import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import no.nordicsemi.android.utils.launchWithCatch import java.util.* import javax.inject.Inject @@ -139,12 +140,12 @@ internal class CGMService : NotificationService() { } private suspend fun configureGatt(services: BleGattServices) { - val glsService = services.findService(CGMS_SERVICE_UUID)!! - val statusCharacteristic = glsService.findCharacteristic(CGM_STATUS_UUID)!! - val featureCharacteristic = glsService.findCharacteristic(CGM_FEATURE_UUID)!! - val measurementCharacteristic = glsService.findCharacteristic(CGM_MEASUREMENT_UUID)!! - val opsControlPointCharacteristic = glsService.findCharacteristic(CGM_OPS_CONTROL_POINT_UUID)!! - recordAccessControlPointCharacteristic = glsService.findCharacteristic(RACP_UUID)!! + val cgmService = services.findService(CGMS_SERVICE_UUID)!! + val statusCharacteristic = cgmService.findCharacteristic(CGM_STATUS_UUID)!! + val featureCharacteristic = cgmService.findCharacteristic(CGM_FEATURE_UUID)!! + val measurementCharacteristic = cgmService.findCharacteristic(CGM_MEASUREMENT_UUID)!! + val opsControlPointCharacteristic = cgmService.findCharacteristic(CGM_OPS_CONTROL_POINT_UUID)!! + recordAccessControlPointCharacteristic = cgmService.findCharacteristic(RACP_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! @@ -194,12 +195,16 @@ internal class CGMService : NotificationService() { .onEach { onAccessControlPointDataReceived(it) } .launchIn(lifecycleScope) - val featuresEnvelope = featureCharacteristic.read().let { CGMFeatureParser.parse(it) }!! - secured = featuresEnvelope.features.e2eCrcSupported + lifecycleScope.launchWithCatch { + val featuresEnvelope = featureCharacteristic.read().let { CGMFeatureParser.parse(it) }!! + secured = featuresEnvelope.features.e2eCrcSupported + } - val statusEnvelope = statusCharacteristic.read().let { CGMStatusParser.parse(it) }!! - if (!statusEnvelope.status.sessionStopped) { - sessionStartTime = System.currentTimeMillis() - statusEnvelope.timeOffset * 60000L + lifecycleScope.launchWithCatch { + val statusEnvelope = statusCharacteristic.read().let { CGMStatusParser.parse(it) }!! + if (!statusEnvelope.status.sessionStopped) { + sessionStartTime = System.currentTimeMillis() - statusEnvelope.timeOffset * 60000L + } } if (sessionStartTime == 0L) { diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt index 8efabf69..590e908b 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt @@ -44,6 +44,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.cgms.R +import no.nordicsemi.android.cgms.data.CGMServiceData import no.nordicsemi.android.cgms.viewmodel.CGMViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView @@ -73,12 +74,12 @@ fun CGMScreen() { if (state.deviceName == null) { DeviceConnectingView() } else { - when (state.result?.connectionState) { + when (state.connectionState) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> CGMContentView(state.result) { viewModel.onEvent(it) } + GattConnectionState.STATE_CONNECTED -> CGMContentView(state) { viewModel.onEvent(it) } } } } @@ -86,7 +87,7 @@ fun CGMScreen() { } @Composable -private fun AppBar(state: CGMViewState, navigateUp: () -> Unit, viewModel: CGMViewModel) { +private fun AppBar(state: CGMServiceData, navigateUp: () -> Unit, viewModel: CGMViewModel) { if (state.deviceName?.isNotBlank() == true) { LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMViewState.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMViewState.kt deleted file mode 100644 index 842f35f7..00000000 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMViewState.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package no.nordicsemi.android.cgms.view - -import no.nordicsemi.android.cgms.data.CGMServiceData - -internal data class CGMViewState( - val result: CGMServiceData? = null, - val deviceName: String? = null -) diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/viewmodel/CGMViewModel.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/viewmodel/CGMViewModel.kt index ac340591..96bbef5c 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/viewmodel/CGMViewModel.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/viewmodel/CGMViewModel.kt @@ -48,7 +48,6 @@ import no.nordicsemi.android.cgms.data.CGMServiceCommand import no.nordicsemi.android.cgms.repository.CGMRepository import no.nordicsemi.android.cgms.repository.CGMS_SERVICE_UUID import no.nordicsemi.android.cgms.view.CGMViewEvent -import no.nordicsemi.android.cgms.view.CGMViewState import no.nordicsemi.android.cgms.view.DisconnectEvent import no.nordicsemi.android.cgms.view.NavigateUp import no.nordicsemi.android.cgms.view.OnWorkingModeSelected @@ -67,8 +66,7 @@ internal class CGMViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(CGMViewState()) - val state = _state.asStateFlow() + val state = repository.data init { viewModelScope.launch { @@ -78,8 +76,6 @@ internal class CGMViewModel @Inject constructor( } repository.data.onEach { - _state.value = _state.value.copy(result = it) - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.CGMS)) } @@ -106,10 +102,14 @@ internal class CGMViewModel @Inject constructor( private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() - is NavigationResult.Success -> repository.launch(result.value) + is NavigationResult.Success -> onDeviceSelected(result.value) } } + private fun onDeviceSelected(device: ServerDevice) { + repository.launch(device) + } + private fun onCommandReceived(workingMode: CGMServiceCommand) { repository.onCommand(workingMode) }