From 1857a60f7a0d0b46e9ec8647b823c9d57f9229ab Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 20 Feb 2023 11:49:07 +0100 Subject: [PATCH 001/101] Update Gradle --- gradle/wrapper/gradle-wrapper.properties | 2 +- lib_scanner/build.gradle.kts | 4 +++- profile_bps/build.gradle.kts | 2 ++ settings.gradle.kts | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b49ef06f..2f37213b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -31,7 +31,7 @@ #Mon Feb 14 14:46:55 CET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/lib_scanner/build.gradle.kts b/lib_scanner/build.gradle.kts index 44b2e683..0d4b00fb 100644 --- a/lib_scanner/build.gradle.kts +++ b/lib_scanner/build.gradle.kts @@ -38,8 +38,10 @@ android { } dependencies { - implementation(libs.nordic.uiscanner) implementation(libs.nordic.navigation) + implementation(libs.nordic.uiscanner) + + implementation("no.nordicsemi.android.kotlin.ble:scanner:0.0.1") implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) diff --git a/profile_bps/build.gradle.kts b/profile_bps/build.gradle.kts index c0d4a0da..a2d32398 100644 --- a/profile_bps/build.gradle.kts +++ b/profile_bps/build.gradle.kts @@ -45,6 +45,8 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) implementation(libs.nordic.navigation) diff --git a/settings.gradle.kts b/settings.gradle.kts index 9178aeed..7d7fa187 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -49,7 +49,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.2.6") + from("no.nordicsemi.android.gradle:version-catalog:1.3.1") } } } From 65c5e483d54afc43ef885066cf3b0ab6d9740d4d Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 22 Feb 2023 15:27:10 +0100 Subject: [PATCH 002/101] Migrate CSC profile to new library --- .../toolbox/scanner/ScannerDestination.kt | 4 +- lib_service/build.gradle.kts | 2 + .../android/service/ServiceManager.kt | 4 +- profile_bps/build.gradle.kts | 1 + .../android/bps/repository/BPSRepository.kt | 23 ++- .../android/bps/view/BPSContentView.kt | 1 - .../android/bps/viewmodel/BPSViewModel.kt | 6 +- profile_cgms/build.gradle.kts | 3 + .../android/cgms/repository/CGMRepository.kt | 24 +-- .../android/cgms/repository/CGMService.kt | 4 +- .../android/cgms/view/CGMContentView.kt | 1 - .../android/cgms/viewmodel/CGMViewModel.kt | 4 +- profile_csc/build.gradle.kts | 3 + .../no/nordicsemi/android/csc/data/CSCData.kt | 43 ------ .../nordicsemi/android/csc/data/CSCManager.kt | 143 ------------------ .../android/csc/data/CSCServicesData.kt | 10 ++ .../nordicsemi/android/csc/data/WheelSize.kt | 39 ----- .../android/csc/repository/CSCRepository.kt | 70 ++++----- .../android/csc/repository/CSCService.kt | 60 +++++++- .../android/csc/view/CSCContentView.kt | 15 +- .../nordicsemi/android/csc/view/CSCMappers.kt | 2 +- .../nordicsemi/android/csc/view/CSCScreen.kt | 28 +--- .../android/csc/view/CSCSettings.kt | 40 ----- .../nordicsemi/android/csc/view/CSCState.kt | 5 +- .../android/csc/view/CSCViewEvent.kt | 2 +- .../android/csc/view/SensorsReadingView.kt | 17 ++- .../android/csc/view/WheelSizeView.kt | 2 +- .../android/csc/viewmodel/CSCViewModel.kt | 9 +- profile_gls/build.gradle.kts | 3 + .../gls/main/viewmodel/GLSViewModel.kt | 7 +- .../android/gls/repository/GLSRepository.kt | 22 +-- profile_hrs/build.gradle.kts | 3 + .../android/hrs/service/HRSRepository.kt | 25 ++- .../android/hrs/service/HRSService.kt | 4 +- .../android/hrs/view/HRSContentView.kt | 1 - .../android/hrs/viewmodel/HRSViewModel.kt | 4 +- profile_hts/build.gradle.kts | 3 + .../android/hts/repository/HTSRepository.kt | 25 ++- .../android/hts/repository/HTSService.kt | 4 +- .../android/hts/view/HTSContentView.kt | 1 - .../android/hts/viewmodel/HTSViewModel.kt | 4 +- profile_prx/build.gradle.kts | 3 + .../android/prx/repository/PRXRepository.kt | 14 +- .../android/prx/repository/PRXService.kt | 4 +- .../android/prx/view/PRXContentView.kt | 1 - .../nordicsemi/android/prx/view/PRXScreen.kt | 1 - .../android/prx/viewmodel/PRXViewModel.kt | 4 +- profile_rscs/build.gradle.kts | 3 + .../android/rscs/repository/RSCSRepository.kt | 25 ++- .../android/rscs/repository/RSCSService.kt | 4 +- .../android/rscs/view/SensorsReadingView.kt | 1 - .../android/rscs/viewmodel/RSCSViewModel.kt | 4 +- profile_uart/build.gradle.kts | 3 + .../nordicsemi/android/uart/db/XmlMacro.java | 5 +- .../android/uart/repository/UARTRepository.kt | 25 ++- .../android/uart/repository/UARTService.kt | 4 +- .../android/uart/viewmodel/UARTViewModel.kt | 4 +- 57 files changed, 273 insertions(+), 503 deletions(-) delete mode 100644 profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCData.kt delete mode 100644 profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCManager.kt create mode 100644 profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt delete mode 100644 profile_csc/src/main/java/no/nordicsemi/android/csc/data/WheelSize.kt delete mode 100644 profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCSettings.kt diff --git a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt index da5cf239..8e86d360 100644 --- a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt +++ b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt @@ -8,9 +8,9 @@ import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewMod import no.nordicsemi.android.common.ui.scanner.DeviceSelected import no.nordicsemi.android.common.ui.scanner.ScannerScreen import no.nordicsemi.android.common.ui.scanner.ScanningCancelled -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice -val ScannerDestinationId = createDestination("uiscanner-destination") +val ScannerDestinationId = createDestination("uiscanner-destination") val ScannerDestination = defineDestination(ScannerDestinationId) { val navigationViewModel = hiltViewModel() diff --git a/lib_service/build.gradle.kts b/lib_service/build.gradle.kts index a58d8cce..784c1105 100644 --- a/lib_service/build.gradle.kts +++ b/lib_service/build.gradle.kts @@ -44,6 +44,8 @@ dependencies { implementation(libs.nordic.ble.ktx) implementation(libs.nordic.uiscanner) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation(libs.androidx.lifecycle.service) implementation(libs.androidx.localbroadcastmanager) implementation(libs.androidx.core) diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt index f2753e6f..faf0c063 100644 --- a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt +++ b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt @@ -35,7 +35,7 @@ import android.bluetooth.BluetoothDevice import android.content.Context import android.content.Intent import dagger.hilt.android.qualifiers.ApplicationContext -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import javax.inject.Inject const val DEVICE_DATA = "device-data" @@ -45,7 +45,7 @@ class ServiceManager @Inject constructor( private val context: Context ) { - fun startService(service: Class, device: DiscoveredBluetoothDevice) { + fun startService(service: Class, device: ServerDevice) { val intent = Intent(context, service).apply { putExtra(DEVICE_DATA, device) } diff --git a/profile_bps/build.gradle.kts b/profile_bps/build.gradle.kts index a2d32398..39c04e70 100644 --- a/profile_bps/build.gradle.kts +++ b/profile_bps/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(project(":lib_utils")) implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSRepository.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSRepository.kt index 527a4db8..aeaaf140 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSRepository.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSRepository.kt @@ -41,12 +41,11 @@ import kotlinx.coroutines.flow.callbackFlow 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.bps.data.BPSData import no.nordicsemi.android.bps.data.BPSManager import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject @@ -61,7 +60,7 @@ internal class BPSRepository @Inject constructor( private var logger: NordicLogger? = null - fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow> = callbackFlow { + fun downloadData(scope: CoroutineScope, device: ServerDevice): Flow> = callbackFlow { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "BPS", device.address).also { logger = it } @@ -81,15 +80,15 @@ internal class BPSRepository @Inject constructor( } } - private suspend fun BPSManager.start(device: DiscoveredBluetoothDevice) { - try { - connect(device.device) - .useAutoConnect(false) - .retry(3, 100) - .suspend() - } catch (e: Exception) { - e.printStackTrace() - } + private suspend fun BPSManager.start(device: ServerDevice) { +// try { +// connect(device.device) +// .useAutoConnect(false) +// .retry(3, 100) +// .suspend() +// } catch (e: Exception) { +// e.printStackTrace() +// } } fun openLogger() { diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSContentView.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSContentView.kt index baf2d1c2..da2621de 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSContentView.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSContentView.kt @@ -34,7 +34,6 @@ package no.nordicsemi.android.bps.view import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 3f79cd58..de180339 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -52,7 +52,7 @@ import no.nordicsemi.android.bps.view.OpenLoggerEvent import no.nordicsemi.android.bps.view.WorkingState import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -75,7 +75,7 @@ internal class BPSViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleArgs(result: NavigationResult) { + private fun handleArgs(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> connectDevice(result.value) @@ -89,7 +89,7 @@ internal class BPSViewModel @Inject constructor( } } - private fun connectDevice(device: DiscoveredBluetoothDevice) { + private fun connectDevice(device: ServerDevice) { repository.downloadData(viewModelScope, device).onEach { _state.value = WorkingState(it) diff --git a/profile_cgms/build.gradle.kts b/profile_cgms/build.gradle.kts index 4c107eff..f444d877 100644 --- a/profile_cgms/build.gradle.kts +++ b/profile_cgms/build.gradle.kts @@ -45,6 +45,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) implementation(libs.nordic.uilogger) 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 43b179eb..f76367d9 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 @@ -45,7 +45,7 @@ import no.nordicsemi.android.cgms.data.CGMData import no.nordicsemi.android.cgms.data.CGMManager import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.ServiceManager @@ -70,11 +70,11 @@ class CGMRepository @Inject constructor( val isRunning = data.map { it.isRunning() } val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } - fun launch(device: DiscoveredBluetoothDevice) { + fun launch(device: ServerDevice) { serviceManager.startService(CGMService::class.java, device) } - fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { + fun start(device: ServerDevice, scope: CoroutineScope) { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CGMS", device.address).also { logger = it } @@ -90,15 +90,15 @@ class CGMRepository @Inject constructor( } } - private suspend fun CGMManager.start(device: DiscoveredBluetoothDevice) { - try { - connect(device.device) - .useAutoConnect(false) - .retry(3, 100) - .suspend() - } catch (e: Exception) { - e.printStackTrace() - } + private suspend fun CGMManager.start(device: ServerDevice) { +// try { +// connect(device.device) +// .useAutoConnect(false) +// .retry(3, 100) +// .suspend() +// } catch (e: Exception) { +// e.printStackTrace() +// } } fun requestAllRecords() { 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 853516d3..9f53dc2d 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 @@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import javax.inject.Inject @@ -50,7 +50,7 @@ internal class CGMService : NotificationService() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) - val device = intent!!.getParcelableExtra(DEVICE_DATA)!! + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! repository.start(device, lifecycleScope) diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMContentView.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMContentView.kt index 7e949d51..bdfacfc0 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMContentView.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMContentView.kt @@ -38,7 +38,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Search 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 6cb09a35..afca2922 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 @@ -57,7 +57,7 @@ import no.nordicsemi.android.cgms.view.OpenLoggerEvent import no.nordicsemi.android.cgms.view.WorkingState import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -105,7 +105,7 @@ internal class CGMViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> repository.launch(result.value) diff --git a/profile_csc/build.gradle.kts b/profile_csc/build.gradle.kts index 3418458c..ac51882f 100644 --- a/profile_csc/build.gradle.kts +++ b/profile_csc/build.gradle.kts @@ -45,6 +45,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) implementation(libs.nordic.uilogger) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCData.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCData.kt deleted file mode 100644 index e9e54649..00000000 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCData.kt +++ /dev/null @@ -1,43 +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.csc.data - -internal data class CSCData( - val scanDevices: Boolean = false, - val speed: Float = 0f, - val cadence: Float = 0f, - val distance: Float = 0f, - val totalDistance: Float = 0f, - val gearRatio: Float = 0f, - val batteryLevel: Int? = null, - val wheelSize: WheelSize = WheelSize() -) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCManager.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCManager.kt deleted file mode 100644 index 5e44daf9..00000000 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCManager.kt +++ /dev/null @@ -1,143 +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.csc.data - -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic -import android.content.Context -import android.util.Log -import kotlinx.coroutines.CoroutineScope -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.battery.BatteryLevelResponse -import no.nordicsemi.android.ble.common.callback.csc.CyclingSpeedAndCadenceMeasurementResponse -import no.nordicsemi.android.ble.ktx.asValidResponseFlow -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.service.ConnectionObserverAdapter -import java.util.* - -val CSC_SERVICE_UUID: UUID = UUID.fromString("00001816-0000-1000-8000-00805f9b34fb") -private val CSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A5B-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 CSCManager( - context: Context, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : BleManager(context) { - - private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null - private var cscMeasurementCharacteristic: BluetoothGattCharacteristic? = null - private var wheelSize: WheelSize = WheelSize() - - private var previousResponse: CyclingSpeedAndCadenceMeasurementResponse? = null - - private val data = MutableStateFlow(CSCData()) - val dataHolder = ConnectionObserverAdapter() - - init { - connectionObserver = dataHolder - - data.onEach { - dataHolder.setValue(it) - }.launchIn(scope) - } - - override fun log(priority: Int, message: String) { - logger.log(priority, message) - } - - override fun getMinLogPriority(): Int { - return Log.VERBOSE - } - - override fun getGattCallback(): BleManagerGattCallback { - return CSCManagerGattCallback() - } - - fun setWheelSize(value: WheelSize) { - wheelSize = value - } - - private inner class CSCManagerGattCallback : BleManagerGattCallback() { - override fun initialize() { - super.initialize() - - setNotificationCallback(cscMeasurementCharacteristic).asValidResponseFlow() - .onEach { - previousResponse?.let { previousResponse -> - val wheelCircumference = wheelSize.value.toFloat() - val totalDistance = it.getTotalDistance(wheelSize.value.toFloat()) - val distance = it.getDistance(wheelCircumference, previousResponse) - val speed = it.getSpeed(wheelCircumference, previousResponse) - val crankCadence = it.getCrankCadence(previousResponse) - val gearRatio = it.getGearRatio(previousResponse) - - data.tryEmit(data.value.copy( - totalDistance = totalDistance, - distance = distance, - speed = speed, - wheelSize = wheelSize, - cadence = crankCadence, - gearRatio = gearRatio, - )) - } - - previousResponse = it - }.launchIn(scope) - enableNotifications(cscMeasurementCharacteristic).enqueue() - - setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow().onEach { - data.value = data.value.copy(batteryLevel = it.batteryLevel) - }.launchIn(scope) - enableNotifications(batteryLevelCharacteristic).enqueue() - } - - public override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - gatt.getService(CSC_SERVICE_UUID)?.run { - cscMeasurementCharacteristic = getCharacteristic(CSC_MEASUREMENT_CHARACTERISTIC_UUID) - } - gatt.getService(BATTERY_SERVICE_UUID)?.run { - batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID) - } - return cscMeasurementCharacteristic != null - } - - override fun onServicesInvalidated() { - cscMeasurementCharacteristic = null - batteryLevelCharacteristic = null - } - } -} diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt new file mode 100644 index 00000000..cfc0f48f --- /dev/null +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt @@ -0,0 +1,10 @@ +package no.nordicsemi.android.csc.data + +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData + +data class CSCServicesData( + val data: CSCData = CSCData(), + val batteryLevel: Int? = null, + val connectionState: GattConnectionState = GattConnectionState.STATE_DISCONNECTED +) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/WheelSize.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/WheelSize.kt deleted file mode 100644 index bfeb2cfc..00000000 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/WheelSize.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.csc.data - -import no.nordicsemi.android.csc.view.CSCSettings - -data class WheelSize( - val value: Int = CSCSettings.DefaultWheelSize.VALUE, - val name: String = CSCSettings.DefaultWheelSize.NAME -) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 3d7bd006..a8a22517 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -33,22 +33,17 @@ package no.nordicsemi.android.csc.repository 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.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import no.nordicsemi.android.ble.ktx.suspend import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice -import no.nordicsemi.android.csc.data.CSCData -import no.nordicsemi.android.csc.data.CSCManager -import no.nordicsemi.android.csc.data.WheelSize -import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.service.IdleResult +import no.nordicsemi.android.csc.data.CSCServicesData +import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData +import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize +import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSizes import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject @@ -62,48 +57,36 @@ class CSCRepository @Inject constructor( private val loggerFactory: NordicLoggerFactory, private val stringConst: StringConst ) { - private var manager: CSCManager? = null private var logger: NordicLogger? = null - private val _data = MutableStateFlow>(IdleResult()) + private val _wheelSize = MutableStateFlow(WheelSizes.default) + internal val wheelSize = _wheelSize.asStateFlow() + + private val _data = MutableStateFlow(CSCServicesData()) internal val data = _data.asStateFlow() - val isRunning = data.map { it.isRunning() } - val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } + val hasBeenDisconnected = + data.map { it.connectionState != GattConnectionState.STATE_CONNECTED && it.connectionState != GattConnectionState.STATE_CONNECTING } - fun launch(device: DiscoveredBluetoothDevice) { + fun launch(device: ServerDevice) { serviceManager.startService(CSCService::class.java, device) } - fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CSC", device.address).also { - logger = it - } - val manager = CSCManager(context, scope, createdLogger) - this.manager = manager - - manager.dataHolder.status.onEach { - _data.value = it - }.launchIn(scope) - - scope.launch { - manager.start(device) - } - } - fun setWheelSize(wheelSize: WheelSize) { - manager?.setWheelSize(wheelSize) + _wheelSize.value = wheelSize } - private suspend fun CSCManager.start(device: DiscoveredBluetoothDevice) { - try { - connect(device.device) - .useAutoConnect(false) - .retry(3, 100) - .suspend() - } catch (e: Exception) { - e.printStackTrace() - } + fun onConnectionStateChanged(connectionState: GattConnectionState) { + _data.value = _data.value.copy(connectionState = connectionState) + } + + fun onBatteryLevelChanged(batteryLevel: Int) { + _data.value = _data.value.copy(batteryLevel = batteryLevel) + } + + fun onCSCDataChanged(cscData: CSCData) { + _data.value = _data.value.copy(data = cscData) } fun openLogger() { @@ -111,8 +94,7 @@ class CSCRepository @Inject constructor( } fun release() { - manager?.disconnect()?.enqueue() logger = null - manager = null + serviceManager.stopService(CSCService::class.java) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 7b9acf43..52e11ca8 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -31,16 +31,32 @@ package no.nordicsemi.android.csc.repository +import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import kotlinx.coroutines.launch +import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.csc.CSCDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import java.util.* import javax.inject.Inject +val CSC_SERVICE_UUID: UUID = UUID.fromString("00001816-0000-1000-8000-00805f9b34fb") +private val CSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A5B-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") + +@SuppressLint("MissingPermission") @AndroidEntryPoint internal class CSCService : NotificationService() { @@ -50,14 +66,44 @@ internal class CSCService : NotificationService() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) - val device = intent!!.getParcelableExtra(DEVICE_DATA)!! + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! - repository.start(device, lifecycleScope) - - repository.hasBeenDisconnected.onEach { - if (it) stopSelf() - }.launchIn(lifecycleScope) + startGattClient(device) return START_REDELIVER_INTENT } + + private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { + val client = blinkyDevice.connect(this@CSCService) + + client.connection + .onEach { repository.onConnectionStateChanged(it.connectionState) } + .map { it.services } + .filterNotNull() + .onEach { configureGatt(it) } + .launchIn(lifecycleScope) + } + + private suspend fun configureGatt(services: BleGattServices) { + val cscService = services.findService(CSC_SERVICE_UUID)!! + val cscMeasurementCharacteristic = cscService.findCharacteristic(CSC_MEASUREMENT_CHARACTERISTIC_UUID)!! + + cscMeasurementCharacteristic.enableNotifications() + + val cscDataParser = CSCDataParser() + cscMeasurementCharacteristic.notification + .mapNotNull { cscDataParser.parse(it, repository.wheelSize.value) } + .onEach { repository.onCSCDataChanged(it) } + .launchIn(lifecycleScope) + + val batteryService = services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + +// batteryLevelCharacteristic.enableNotifications() + + batteryLevelCharacteristic.notification + .mapNotNull { BatteryLevelParser.parse(it) } + .onEach { repository.onBatteryLevelChanged(it) } + .launchIn(lifecycleScope) + } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt index 1bc80f35..e8133ab8 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt @@ -34,7 +34,6 @@ package no.nordicsemi.android.csc.view import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Button @@ -50,15 +49,16 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.view.RadioButtonGroup import no.nordicsemi.android.csc.R -import no.nordicsemi.android.csc.data.CSCData -import no.nordicsemi.android.csc.data.WheelSize +import no.nordicsemi.android.csc.data.CSCServicesData +import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData +import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle import no.nordicsemi.android.ui.view.dialog.FlowCanceled import no.nordicsemi.android.ui.view.dialog.ItemSelectedResult @Composable -internal fun CSCContentView(state: CSCData, speedUnit: SpeedUnit, onEvent: (CSCViewEvent) -> Unit) { +internal fun CSCContentView(state: CSCServicesData, speedUnit: SpeedUnit, onEvent: (CSCViewEvent) -> Unit) { val showDialog = rememberSaveable { mutableStateOf(false) } if (showDialog.value) { @@ -69,8 +69,7 @@ internal fun CSCContentView(state: CSCData, speedUnit: SpeedUnit, onEvent: (CSCV when (it) { FlowCanceled -> showDialog.value = false is ItemSelectedResult -> { - onEvent(OnWheelSizeSelected(WheelSize(wheelValues[it.index].toInt(), - wheelEntries[it.index]))) + onEvent(OnWheelSizeSelected(WheelSize(wheelValues[it.index].toInt(), wheelEntries[it.index]))) showDialog.value = false } } @@ -80,7 +79,7 @@ internal fun CSCContentView(state: CSCData, speedUnit: SpeedUnit, onEvent: (CSCV Column( horizontalAlignment = Alignment.CenterHorizontally, ) { - SettingsSection(state, speedUnit, onEvent) { showDialog.value = true } + SettingsSection(state.data, speedUnit, onEvent) { showDialog.value = true } Spacer(modifier = Modifier.height(16.dp)) @@ -126,5 +125,5 @@ private fun SettingsSection( @Preview @Composable private fun ConnectedPreview() { - CSCContentView(CSCData(), SpeedUnit.KM_H) { } + CSCContentView(CSCServicesData(), SpeedUnit.KM_H) { } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt index 43589f94..0a186798 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt @@ -33,7 +33,7 @@ package no.nordicsemi.android.csc.view import no.nordicsemi.android.common.theme.view.RadioButtonItem import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity -import no.nordicsemi.android.csc.data.CSCData +import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData import java.util.* private const val DISPLAY_M_S = "m/s" diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index e7980385..89443dc4 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -48,15 +48,8 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.viewmodel.CSCViewModel -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -80,20 +73,11 @@ fun CSCScreen() { ) { when (state.cscManagerState) { NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.cscManagerState.result) { - is IdleResult, - is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) } - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { - NavigateUpButton(navigateUp) - } - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - is SuccessResult -> CSCContentView( - state.cscManagerState.result.data, - state.speedUnit - ) { viewModel.onEvent(it) } + is WorkingState -> when (state.cscManagerState.result.connectionState) { + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> CSCContentView(state.cscManagerState.result, state.speedUnit) { viewModel.onEvent(it) } } } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCSettings.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCSettings.kt deleted file mode 100644 index 9b9a362c..00000000 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCSettings.kt +++ /dev/null @@ -1,40 +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.csc.view - -internal object CSCSettings { - - object DefaultWheelSize { - const val NAME = "60-622" - const val VALUE = 2340 - } -} \ No newline at end of file diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt index d6f2de13..dc2840aa 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt @@ -31,8 +31,7 @@ package no.nordicsemi.android.csc.view -import no.nordicsemi.android.csc.data.CSCData -import no.nordicsemi.android.service.BleManagerResult +import no.nordicsemi.android.csc.data.CSCServicesData internal data class CSCViewState( val speedUnit: SpeedUnit = SpeedUnit.M_S, @@ -41,6 +40,6 @@ internal data class CSCViewState( internal sealed class CSCMangerState -internal data class WorkingState(val result: BleManagerResult) : CSCMangerState() +internal data class WorkingState(val result: CSCServicesData) : CSCMangerState() internal object NoDeviceState : CSCMangerState() diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt index 6cc033b7..b0ba0c84 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt @@ -31,7 +31,7 @@ package no.nordicsemi.android.csc.view -import no.nordicsemi.android.csc.data.WheelSize +import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize internal sealed class CSCViewEvent diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt index 123af1c8..cfa0c373 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt @@ -40,32 +40,33 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.csc.R -import no.nordicsemi.android.csc.data.CSCData +import no.nordicsemi.android.csc.data.CSCServicesData import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun SensorsReadingView(state: CSCData, speedUnit: SpeedUnit) { +internal fun SensorsReadingView(state: CSCServicesData, speedUnit: SpeedUnit) { + val csc = state.data ScreenSection { SectionTitle(resId = R.drawable.ic_records, title = "Records") Spacer(modifier = Modifier.height(16.dp)) Column { - KeyValueField(stringResource(id = R.string.csc_field_speed), state.displaySpeed(speedUnit)) + KeyValueField(stringResource(id = R.string.csc_field_speed), csc.displaySpeed(speedUnit)) Spacer(modifier = Modifier.height(4.dp)) - KeyValueField(stringResource(id = R.string.csc_field_cadence), state.displayCadence()) + KeyValueField(stringResource(id = R.string.csc_field_cadence), csc.displayCadence()) Spacer(modifier = Modifier.height(4.dp)) - KeyValueField(stringResource(id = R.string.csc_field_distance), state.displayDistance(speedUnit)) + KeyValueField(stringResource(id = R.string.csc_field_distance), csc.displayDistance(speedUnit)) Spacer(modifier = Modifier.height(4.dp)) KeyValueField( stringResource(id = R.string.csc_field_total_distance), - state.displayTotalDistance(speedUnit) + csc.displayTotalDistance(speedUnit) ) Spacer(modifier = Modifier.height(4.dp)) - KeyValueField(stringResource(id = R.string.csc_field_gear_ratio), state.displayGearRatio()) + KeyValueField(stringResource(id = R.string.csc_field_gear_ratio), csc.displayGearRatio()) } } @@ -79,5 +80,5 @@ internal fun SensorsReadingView(state: CSCData, speedUnit: SpeedUnit) { @Preview @Composable private fun Preview() { - SensorsReadingView(CSCData(), SpeedUnit.KM_H) + SensorsReadingView(CSCServicesData(), SpeedUnit.KM_H) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt index dd94c9a3..98393d66 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt @@ -47,7 +47,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.csc.R -import no.nordicsemi.android.csc.data.CSCData +import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData @Composable internal fun WheelSizeView(state: CSCData, onClick: () -> Unit) { diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index 8f5d333b..58f2afa9 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -46,9 +46,8 @@ import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice -import no.nordicsemi.android.csc.data.CSC_SERVICE_UUID import no.nordicsemi.android.csc.repository.CSCRepository +import no.nordicsemi.android.csc.repository.CSC_SERVICE_UUID import no.nordicsemi.android.csc.view.CSCViewEvent import no.nordicsemi.android.csc.view.CSCViewState import no.nordicsemi.android.csc.view.NavigateUp @@ -58,6 +57,8 @@ import no.nordicsemi.android.csc.view.OnWheelSizeSelected import no.nordicsemi.android.csc.view.OpenLogger import no.nordicsemi.android.csc.view.SpeedUnit import no.nordicsemi.android.csc.view.WorkingState +import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -82,7 +83,7 @@ internal class CSCViewModel @Inject constructor( repository.data.onEach { _state.value = _state.value.copy(cscManagerState = WorkingState(it)) - (it as? ConnectedResult)?.let { + if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.CSC)) } }.launchIn(viewModelScope) @@ -96,7 +97,7 @@ internal class CSCViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> repository.launch(result.value) diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index 5a2d4e70..ed4598d9 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -45,6 +45,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.chart) implementation(libs.nordic.ble.common) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index a789d575..7513f8c9 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -32,7 +32,6 @@ package no.nordicsemi.android.gls.main.viewmodel import android.os.ParcelUuid -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -45,7 +44,6 @@ import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.gls.GlsDetailsDestinationId import no.nordicsemi.android.gls.data.GLS_SERVICE_UUID import no.nordicsemi.android.gls.main.view.DisconnectEvent @@ -57,6 +55,7 @@ import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected import no.nordicsemi.android.gls.main.view.OpenLoggerEvent import no.nordicsemi.android.gls.main.view.WorkingState import no.nordicsemi.android.gls.repository.GLSRepository +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -79,7 +78,7 @@ internal class GLSViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> connectDevice(result.value) @@ -96,7 +95,7 @@ internal class GLSViewModel @Inject constructor( } } - private fun connectDevice(device: DiscoveredBluetoothDevice) { + private fun connectDevice(device: ServerDevice) { repository.downloadData(viewModelScope, device).onEach { _state.value = WorkingState(it) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt index cbf5c576..86050cca 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt @@ -44,10 +44,10 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.ble.ktx.suspend import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.gls.data.GLSData import no.nordicsemi.android.gls.data.GLSManager import no.nordicsemi.android.gls.data.WorkingMode +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject @@ -63,7 +63,7 @@ internal class GLSRepository @Inject constructor( private var manager: GLSManager? = null private var logger: NordicLogger? = null - fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow> = callbackFlow { + fun downloadData(scope: CoroutineScope, device: ServerDevice): Flow> = callbackFlow { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "GLS", device.address).also { logger = it } @@ -87,15 +87,15 @@ internal class GLSRepository @Inject constructor( } } - private suspend fun GLSManager.start(device: DiscoveredBluetoothDevice) { - try { - connect(device.device) - .useAutoConnect(false) - .retry(3, 100) - .suspend() - } catch (e: Exception) { - e.printStackTrace() - } + private suspend fun GLSManager.start(device: ServerDevice) { +// try { +// connect(device.device) +// .useAutoConnect(false) +// .retry(3, 100) +// .suspend() +// } catch (e: Exception) { +// e.printStackTrace() +// } } fun openLogger() { diff --git a/profile_hrs/build.gradle.kts b/profile_hrs/build.gradle.kts index 5ebcb6f0..ea8177d8 100644 --- a/profile_hrs/build.gradle.kts +++ b/profile_hrs/build.gradle.kts @@ -45,6 +45,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.chart) implementation(libs.nordic.theme) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index b12efe66..85d0928f 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -40,12 +40,11 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.ble.ktx.suspend import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.hrs.data.HRSData import no.nordicsemi.android.hrs.data.HRSManager +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.ServiceManager @@ -70,11 +69,11 @@ class HRSRepository @Inject constructor( val isRunning = data.map { it.isRunning() } val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } - fun launch(device: DiscoveredBluetoothDevice) { + fun launch(device: ServerDevice) { serviceManager.startService(HRSService::class.java, device) } - fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { + fun start(device: ServerDevice, scope: CoroutineScope) { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HRS", device.address).also { logger = it } @@ -94,15 +93,15 @@ class HRSRepository @Inject constructor( NordicLogger.launch(context, logger) } - private suspend fun HRSManager.start(device: DiscoveredBluetoothDevice) { - try { - connect(device.device) - .useAutoConnect(false) - .retry(3, 100) - .suspend() - } catch (e: Exception) { - e.printStackTrace() - } + private suspend fun HRSManager.start(device: ServerDevice) { +// try { +// connect(device.device) +// .useAutoConnect(false) +// .retry(3, 100) +// .suspend() +// } catch (e: Exception) { +// e.printStackTrace() +// } } fun release() { diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index fce7e486..a3d7675d 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import javax.inject.Inject @@ -50,7 +50,7 @@ internal class HRSService : NotificationService() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) - val device = intent!!.getParcelableExtra(DEVICE_DATA)!! + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! repository.start(device, lifecycleScope) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt index 4cd10842..d7e96190 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt @@ -34,7 +34,6 @@ package no.nordicsemi.android.hrs.view import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index 5f276cd6..0ebe4367 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -46,7 +46,6 @@ import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.hrs.data.HRS_SERVICE_UUID import no.nordicsemi.android.hrs.service.HRSRepository import no.nordicsemi.android.hrs.view.DisconnectEvent @@ -57,6 +56,7 @@ import no.nordicsemi.android.hrs.view.NoDeviceState import no.nordicsemi.android.hrs.view.OpenLoggerEvent import no.nordicsemi.android.hrs.view.SwitchZoomEvent import no.nordicsemi.android.hrs.view.WorkingState +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -96,7 +96,7 @@ internal class HRSViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> repository.launch(result.value) diff --git a/profile_hts/build.gradle.kts b/profile_hts/build.gradle.kts index 7bafc1e2..7b8423eb 100644 --- a/profile_hts/build.gradle.kts +++ b/profile_hts/build.gradle.kts @@ -45,6 +45,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 16e1f9f3..a6b88dd3 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -40,12 +40,11 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.ble.ktx.suspend import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.hts.data.HTSData import no.nordicsemi.android.hts.data.HTSManager +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.ServiceManager @@ -70,11 +69,11 @@ class HTSRepository @Inject constructor( val isRunning = data.map { it.isRunning() } val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } - fun launch(device: DiscoveredBluetoothDevice) { + fun launch(device: ServerDevice) { serviceManager.startService(HTSService::class.java, device) } - fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { + fun start(device: ServerDevice, scope: CoroutineScope) { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HTS", device.address).also { logger = it } @@ -94,15 +93,15 @@ class HTSRepository @Inject constructor( NordicLogger.launch(context, logger) } - private suspend fun HTSManager.start(device: DiscoveredBluetoothDevice) { - try { - connect(device.device) - .useAutoConnect(false) - .retry(3, 100) - .suspend() - } catch (e: Exception) { - e.printStackTrace() - } + private suspend fun HTSManager.start(device: ServerDevice) { +// try { +// connect(device.device) +// .useAutoConnect(false) +// .retry(3, 100) +// .suspend() +// } catch (e: Exception) { +// e.printStackTrace() +// } } fun release() { diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 50bb962e..05ff5dbb 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import javax.inject.Inject @@ -50,7 +50,7 @@ internal class HTSService : NotificationService() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) - val device = intent!!.getParcelableExtra(DEVICE_DATA)!! + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! repository.start(device, lifecycleScope) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt index 8858ab9d..4c2242b6 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt @@ -35,7 +35,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index 1ffb4bd4..d11c8f85 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -46,7 +46,6 @@ import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.hts.data.HTS_SERVICE_UUID import no.nordicsemi.android.hts.repository.HTSRepository import no.nordicsemi.android.hts.view.DisconnectEvent @@ -56,6 +55,7 @@ import no.nordicsemi.android.hts.view.NavigateUp import no.nordicsemi.android.hts.view.OnTemperatureUnitSelected import no.nordicsemi.android.hts.view.OpenLoggerEvent import no.nordicsemi.android.hts.view.WorkingState +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -94,7 +94,7 @@ internal class HTSViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> repository.launch(result.value) diff --git a/profile_prx/build.gradle.kts b/profile_prx/build.gradle.kts index f8585a6f..ade0bcee 100644 --- a/profile_prx/build.gradle.kts +++ b/profile_prx/build.gradle.kts @@ -45,6 +45,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 9a9c42b6..20b4c13e 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -41,7 +41,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.prx.data.AlarmLevel import no.nordicsemi.android.prx.data.PRXData import no.nordicsemi.android.prx.data.PRXManager @@ -75,12 +75,12 @@ class PRXRepository @Inject internal constructor( val isRunning = data.map { it.isRunning() } val hasBeenDisconnectedWithoutLinkLoss = data.map { it.hasBeenDisconnectedWithoutLinkLoss() } - fun launch(device: DiscoveredBluetoothDevice) { + fun launch(device: ServerDevice) { serviceManager.startService(PRXService::class.java, device) proximityServerManager.open() } - fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { + fun start(device: ServerDevice, scope: CoroutineScope) { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "PRX", device.address).also { logger = it } @@ -93,10 +93,10 @@ class PRXRepository @Inject internal constructor( handleLocalAlarm(it) }.launchIn(scope) - manager.connect(device.device) - .useAutoConnect(true) - .retry(3, 100) - .enqueue() +// manager.connect(device.device) +// .useAutoConnect(true) +// .retry(3, 100) +// .enqueue() } private fun handleLocalAlarm(result: BleManagerResult) { diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 1b19f49e..9925bf4b 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import javax.inject.Inject @@ -50,7 +50,7 @@ internal class PRXService : NotificationService() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) - val device = intent!!.getParcelableExtra(DEVICE_DATA)!! + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! repository.start(device, lifecycleScope) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt index d7da2a3b..e47fa8b1 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt @@ -34,7 +34,6 @@ package no.nordicsemi.android.prx.view import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Button diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index 933db8a7..b20d2f3c 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -39,7 +39,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index 2143bfee..6acfe572 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -46,7 +46,7 @@ import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.prx.data.PRX_SERVICE_UUID import no.nordicsemi.android.prx.repository.PRXRepository import no.nordicsemi.android.prx.view.DisconnectEvent @@ -96,7 +96,7 @@ internal class PRXViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> repository.launch(result.value) diff --git a/profile_rscs/build.gradle.kts b/profile_rscs/build.gradle.kts index 92d50647..360d8826 100644 --- a/profile_rscs/build.gradle.kts +++ b/profile_rscs/build.gradle.kts @@ -45,6 +45,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index fe7af444..1e270afb 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -40,10 +40,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.ble.ktx.suspend import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.rscs.data.RSCSData import no.nordicsemi.android.rscs.data.RSCSManager import no.nordicsemi.android.service.BleManagerResult @@ -70,11 +69,11 @@ class RSCSRepository @Inject constructor( val isRunning = data.map { it.isRunning() } val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } - fun launch(device: DiscoveredBluetoothDevice) { + fun launch(device: ServerDevice) { serviceManager.startService(RSCSService::class.java, device) } - fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { + fun start(device: ServerDevice, scope: CoroutineScope) { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "RSCS", device.address).also { logger = it } @@ -94,15 +93,15 @@ class RSCSRepository @Inject constructor( NordicLogger.launch(context, logger) } - private suspend fun RSCSManager.start(device: DiscoveredBluetoothDevice) { - try { - connect(device.device) - .useAutoConnect(false) - .retry(3, 100) - .suspend() - } catch (e: Exception) { - e.printStackTrace() - } + private suspend fun RSCSManager.start(device: ServerDevice) { +// try { +// connect(device.device) +// .useAutoConnect(false) +// .retry(3, 100) +// .suspend() +// } catch (e: Exception) { +// e.printStackTrace() +// } } fun release() { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index 4359dab3..acdb6120 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import javax.inject.Inject @@ -50,7 +50,7 @@ internal class RSCSService : NotificationService() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) - val device = intent!!.getParcelableExtra(DEVICE_DATA)!! + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! repository.start(device, lifecycleScope) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt index 1c89e3f9..241c1c1e 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt @@ -33,7 +33,6 @@ package no.nordicsemi.android.rscs.view import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.material3.OutlinedCard import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 2dbbe12a..78360059 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -46,7 +46,7 @@ import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.rscs.data.RSCS_SERVICE_UUID import no.nordicsemi.android.rscs.repository.RSCSRepository import no.nordicsemi.android.rscs.view.DisconnectEvent @@ -94,7 +94,7 @@ internal class RSCSViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> repository.launch(result.value) diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index eda73918..8890e92e 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -51,6 +51,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.room.runtime) implementation(libs.room.ktx) kapt(libs.room.compiler) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.java b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.java index 202097bd..726b9b34 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.java +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.java @@ -35,9 +35,8 @@ import org.simpleframework.xml.Attribute; import org.simpleframework.xml.Root; import org.simpleframework.xml.Text; -//import no.nordicsemi.android.uart.data.MacroEol; -//import no.nordicsemi.android.uart.data.MacroIcon; -import no.nordicsemi.android.uart.data.*; +import no.nordicsemi.android.uart.data.MacroEol; +import no.nordicsemi.android.uart.data.MacroIcon; @Root public class XmlMacro { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index a01b1bcf..47deff88 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -40,10 +40,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.ble.ktx.suspend import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.ServiceManager @@ -78,11 +77,11 @@ class UARTRepository @Inject internal constructor( val lastConfigurationName = configurationDataSource.lastConfigurationName - fun launch(device: DiscoveredBluetoothDevice) { + fun launch(device: ServerDevice) { serviceManager.startService(UARTService::class.java, device) } - fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { + fun start(device: ServerDevice, scope: CoroutineScope) { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "UART", device.address).also { logger = it } @@ -119,15 +118,15 @@ class UARTRepository @Inject internal constructor( configurationDataSource.saveConfigurationName(name) } - private suspend fun UARTManager.start(device: DiscoveredBluetoothDevice) { - try { - connect(device.device) - .useAutoConnect(false) - .retry(3, 100) - .suspend() - } catch (e: Exception) { - e.printStackTrace() - } + private suspend fun UARTManager.start(device: ServerDevice) { +// try { +// connect(device.device) +// .useAutoConnect(false) +// .retry(3, 100) +// .suspend() +// } catch (e: Exception) { +// e.printStackTrace() +// } } fun release() { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index da4e4283..611fa329 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import javax.inject.Inject @@ -50,7 +50,7 @@ internal class UARTService : NotificationService() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) - val device = intent!!.getParcelableExtra(DEVICE_DATA)!! + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! repository.start(device, lifecycleScope) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index 7d736e37..9e3a0409 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -51,7 +51,7 @@ import no.nordicsemi.android.analytics.UARTMode import no.nordicsemi.android.analytics.UARTSendAnalyticsEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId @@ -129,7 +129,7 @@ internal class UARTViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> repository.launch(result.value) From 260eafb678be9b725557e2fe9dbf2129c95c9e71 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 24 Feb 2023 16:32:38 +0100 Subject: [PATCH 003/101] Update Gradle --- build.gradle.kts | 4 +-- .../android/csc/repository/CSCService.kt | 27 +++++++++---------- settings.gradle.kts | 8 +++--- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 403f8311..fa8c025b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,6 +45,6 @@ plugins { alias(libs.plugins.nordic.library.compose) apply false alias(libs.plugins.nordic.hilt) apply false alias(libs.plugins.nordic.feature) apply false - id("com.google.gms.google-services") version "4.3.15" apply false - id("com.google.firebase.crashlytics") version "2.9.2" apply false + alias(libs.plugins.google.services) apply false + alias(libs.plugins.firebase.crashlytics) apply false } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 52e11ca8..6ff276d7 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -37,7 +37,6 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -76,9 +75,11 @@ internal class CSCService : NotificationService() { private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { val client = blinkyDevice.connect(this@CSCService) - client.connection - .onEach { repository.onConnectionStateChanged(it.connectionState) } - .map { it.services } + client.connectionState + .onEach { repository.onConnectionStateChanged(it) } + .launchIn(lifecycleScope) + + client.services .filterNotNull() .onEach { configureGatt(it) } .launchIn(lifecycleScope) @@ -87,23 +88,19 @@ internal class CSCService : NotificationService() { private suspend fun configureGatt(services: BleGattServices) { val cscService = services.findService(CSC_SERVICE_UUID)!! val cscMeasurementCharacteristic = cscService.findCharacteristic(CSC_MEASUREMENT_CHARACTERISTIC_UUID)!! - - cscMeasurementCharacteristic.enableNotifications() - - val cscDataParser = CSCDataParser() - cscMeasurementCharacteristic.notification - .mapNotNull { cscDataParser.parse(it, repository.wheelSize.value) } - .onEach { repository.onCSCDataChanged(it) } - .launchIn(lifecycleScope) - val batteryService = services.findService(BATTERY_SERVICE_UUID)!! val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! -// batteryLevelCharacteristic.enableNotifications() - batteryLevelCharacteristic.notification .mapNotNull { BatteryLevelParser.parse(it) } .onEach { repository.onBatteryLevelChanged(it) } .launchIn(lifecycleScope) + + TODO("Second notification not working") + val cscDataParser = CSCDataParser() + cscMeasurementCharacteristic.notification + .mapNotNull { cscDataParser.parse(it, repository.wheelSize.value) } + .onEach { repository.onCSCDataChanged(it) } + .launchIn(lifecycleScope) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7d7fa187..b0a6d835 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -77,7 +77,7 @@ include(":lib_utils") //if (file("../Android-Common-Libraries").exists()) { // includeBuild("../Android-Common-Libraries") //} -// -//if (file('../Android-BLE-Library').exists()) { -// includeBuild('../Android-BLE-Library') -//} + +if (file("../Kotlin-BLE-Library").exists()) { + includeBuild("../Kotlin-BLE-Library") +} From f54d4c2360dd0d65d62be035c0fa303e8d8fdb62 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 6 Mar 2023 12:32:47 +0100 Subject: [PATCH 004/101] Fix issue with multiple notifications on many characteristics --- .../java/no/nordicsemi/android/csc/repository/CSCService.kt | 5 ++--- .../java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 6ff276d7..119c5a27 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -91,14 +91,13 @@ internal class CSCService : NotificationService() { val batteryService = services.findService(BATTERY_SERVICE_UUID)!! val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! - batteryLevelCharacteristic.notification + batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } .onEach { repository.onBatteryLevelChanged(it) } .launchIn(lifecycleScope) - TODO("Second notification not working") val cscDataParser = CSCDataParser() - cscMeasurementCharacteristic.notification + cscMeasurementCharacteristic.getNotifications() .mapNotNull { cscDataParser.parse(it, repository.wheelSize.value) } .onEach { repository.onCSCDataChanged(it) } .launchIn(lifecycleScope) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index 58f2afa9..c3fb8628 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -59,7 +59,6 @@ import no.nordicsemi.android.csc.view.SpeedUnit import no.nordicsemi.android.csc.view.WorkingState import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject From 7630a3113b5f89a12d78bf01adf1dec40afcc42b Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 7 Mar 2023 16:04:23 +0100 Subject: [PATCH 005/101] Fix scanning --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../android/toolbox/scanner/ScannerDestination.kt | 3 ++- .../no/nordicsemi/android/csc/repository/CSCRepository.kt | 6 ------ settings.gradle.kts | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2f37213b..16581bec 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -31,7 +31,7 @@ #Mon Feb 14 14:46:55 CET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt index 8e86d360..5fb80fb9 100644 --- a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt +++ b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt @@ -9,6 +9,7 @@ import no.nordicsemi.android.common.ui.scanner.DeviceSelected import no.nordicsemi.android.common.ui.scanner.ScannerScreen import no.nordicsemi.android.common.ui.scanner.ScanningCancelled import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import java.util.* val ScannerDestinationId = createDestination("uiscanner-destination") @@ -18,7 +19,7 @@ val ScannerDestination = defineDestination(ScannerDestinationId) { val arg = navigationViewModel.parameterOf(ScannerDestinationId) ScannerScreen( - uuid = arg, + uuid = ParcelUuid(UUID.fromString("00001816-0000-1000-8000-00805f9b34fb")), onResult = { when (it) { is DeviceSelected -> navigationViewModel.navigateUpWithResult(ScannerDestinationId, it.device) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index a8a22517..85f67a79 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -37,7 +37,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.csc.data.CSCServicesData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState @@ -45,7 +44,6 @@ import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSizes import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -54,8 +52,6 @@ class CSCRepository @Inject constructor( @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst ) { private var logger: NordicLogger? = null @@ -66,8 +62,6 @@ class CSCRepository @Inject constructor( internal val data = _data.asStateFlow() val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } - val hasBeenDisconnected = - data.map { it.connectionState != GattConnectionState.STATE_CONNECTED && it.connectionState != GattConnectionState.STATE_CONNECTING } fun launch(device: ServerDevice) { serviceManager.startService(CSCService::class.java, device) diff --git a/settings.gradle.kts b/settings.gradle.kts index b0a6d835..8383a4f7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -49,7 +49,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.3.1") + from("no.nordicsemi.android.gradle:version-catalog:1.3.3") } } } From 9d74000a036c28659e8b26247fe74972edec6275 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 8 Mar 2023 16:19:19 +0100 Subject: [PATCH 006/101] Migrate HTS profile to the new BLE library --- .../android/service/DisconnectAndStopEvent.kt | 3 + profile_csc/build.gradle.kts | 1 + .../android/csc/data/CSCServicesData.kt | 2 +- .../android/csc/repository/CSCRepository.kt | 10 +- .../android/csc/repository/CSCService.kt | 22 +++- .../nordicsemi/android/csc/view/CSCScreen.kt | 14 +-- .../nordicsemi/android/csc/view/CSCState.kt | 3 +- .../android/csc/viewmodel/CSCViewModel.kt | 7 +- profile_hts/build.gradle.kts | 1 + .../nordicsemi/android/hts/data/HTSManager.kt | 119 ------------------ .../data/{HTSData.kt => HTSServicesData.kt} | 8 +- .../android/hts/repository/HTSRepository.kt | 63 ++++------ .../android/hts/repository/HTSService.kt | 69 +++++++++- .../android/hts/view/HTSContentView.kt | 8 +- .../nordicsemi/android/hts/view/HTSScreen.kt | 38 ++---- .../nordicsemi/android/hts/view/HTSState.kt | 8 +- .../android/hts/viewmodel/HTSViewModel.kt | 13 +- 17 files changed, 168 insertions(+), 221 deletions(-) create mode 100644 lib_service/src/main/java/no/nordicsemi/android/service/DisconnectAndStopEvent.kt delete mode 100644 profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSManager.kt rename profile_hts/src/main/java/no/nordicsemi/android/hts/data/{HTSData.kt => HTSServicesData.kt} (86%) diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/DisconnectAndStopEvent.kt b/lib_service/src/main/java/no/nordicsemi/android/service/DisconnectAndStopEvent.kt new file mode 100644 index 00000000..31bdb0ee --- /dev/null +++ b/lib_service/src/main/java/no/nordicsemi/android/service/DisconnectAndStopEvent.kt @@ -0,0 +1,3 @@ +package no.nordicsemi.android.service + +class DisconnectAndStopEvent diff --git a/profile_csc/build.gradle.kts b/profile_csc/build.gradle.kts index ac51882f..016ed175 100644 --- a/profile_csc/build.gradle.kts +++ b/profile_csc/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(libs.nordic.theme) implementation(libs.nordic.navigation) implementation(libs.nordic.uiscanner) + implementation(libs.nordic.core) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt index cfc0f48f..f1ebb66e 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt @@ -6,5 +6,5 @@ import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData data class CSCServicesData( val data: CSCData = CSCData(), val batteryLevel: Int? = null, - val connectionState: GattConnectionState = GattConnectionState.STATE_DISCONNECTED + val connectionState: GattConnectionState? = null ) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 85f67a79..9f4103df 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -34,8 +34,10 @@ package no.nordicsemi.android.csc.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map +import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.csc.data.CSCServicesData import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -43,6 +45,7 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSizes +import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject import javax.inject.Singleton @@ -61,6 +64,9 @@ class CSCRepository @Inject constructor( private val _data = MutableStateFlow(CSCServicesData()) internal val data = _data.asStateFlow() + private val _stopEvent = simpleSharedFlow() + internal val stopEvent = _stopEvent.asSharedFlow() + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { @@ -71,7 +77,7 @@ class CSCRepository @Inject constructor( _wheelSize.value = wheelSize } - fun onConnectionStateChanged(connectionState: GattConnectionState) { + fun onConnectionStateChanged(connectionState: GattConnectionState?) { _data.value = _data.value.copy(connectionState = connectionState) } @@ -89,6 +95,6 @@ class CSCRepository @Inject constructor( fun release() { logger = null - serviceManager.stopService(CSCService::class.java) + _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 119c5a27..89c4a506 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -41,7 +41,9 @@ import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.csc.CSCDataParser import no.nordicsemi.android.service.DEVICE_DATA @@ -62,6 +64,8 @@ internal class CSCService : NotificationService() { @Inject lateinit var repository: CSCRepository + private lateinit var client: BleGattClient + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -69,14 +73,20 @@ internal class CSCService : NotificationService() { startGattClient(device) + repository.stopEvent + .onEach { disconnect() } + .launchIn(lifecycleScope) + return START_REDELIVER_INTENT } private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { - val client = blinkyDevice.connect(this@CSCService) + client = blinkyDevice.connect(this@CSCService) client.connectionState .onEach { repository.onConnectionStateChanged(it) } + .filterNotNull() + .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) client.services @@ -102,4 +112,14 @@ internal class CSCService : NotificationService() { .onEach { repository.onCSCDataChanged(it) } .launchIn(lifecycleScope) } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + stopSelf() + } + } + + private fun disconnect() { + client.disconnect() + } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 89443dc4..003d895b 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -49,7 +49,6 @@ import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.viewmodel.CSCViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.service.DeviceHolder import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -74,6 +73,7 @@ fun CSCScreen() { when (state.cscManagerState) { NoDeviceState -> DeviceConnectingView() is WorkingState -> when (state.cscManagerState.result.connectionState) { + null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } @@ -86,15 +86,11 @@ fun CSCScreen() { @Composable private fun AppBar(state: CSCViewState, navigateUp: () -> Unit, viewModel: CSCViewModel) { - val toolbarName = (state.cscManagerState as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { - BackIconAppBar(stringResource(id = R.string.csc_title), navigateUp) - } else { - LoggerIconAppBar(toolbarName, navigateUp, { viewModel.onEvent(OnDisconnectButtonClick) }) { + if (state.deviceName?.isNotBlank() == true) { + LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(OnDisconnectButtonClick) }) { viewModel.onEvent(OpenLogger) } + } else { + BackIconAppBar(stringResource(id = R.string.csc_title), navigateUp) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt index dc2840aa..9fbbe674 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt @@ -35,7 +35,8 @@ import no.nordicsemi.android.csc.data.CSCServicesData internal data class CSCViewState( val speedUnit: SpeedUnit = SpeedUnit.M_S, - val cscManagerState: CSCMangerState = NoDeviceState + val cscManagerState: CSCMangerState = NoDeviceState, + val deviceName: String? = null ) internal sealed class CSCMangerState diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index c3fb8628..b6a0c75e 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -99,10 +99,15 @@ internal class CSCViewModel @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) { + _state.value = _state.value.copy(deviceName = device.name) + repository.launch(device) + } + fun onEvent(event: CSCViewEvent) { when (event) { is OnSelectedSpeedUnitSelected -> setSpeedUnit(event.selectedSpeedUnit) diff --git a/profile_hts/build.gradle.kts b/profile_hts/build.gradle.kts index 7b8423eb..66296ad1 100644 --- a/profile_hts/build.gradle.kts +++ b/profile_hts/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(libs.nordic.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.nordic.core) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSManager.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSManager.kt deleted file mode 100644 index 5d4e8cb2..00000000 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSManager.kt +++ /dev/null @@ -1,119 +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.hts.data - -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic -import android.content.Context -import android.util.Log -import kotlinx.coroutines.CoroutineScope -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.battery.BatteryLevelResponse -import no.nordicsemi.android.ble.common.callback.ht.TemperatureMeasurementResponse -import no.nordicsemi.android.ble.ktx.asValidResponseFlow -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.service.ConnectionObserverAdapter -import java.util.* - -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 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( - context: Context, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : BleManager(context) { - - private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null - private var htCharacteristic: BluetoothGattCharacteristic? = null - - private val data = MutableStateFlow(HTSData()) - val dataHolder = ConnectionObserverAdapter() - - init { - connectionObserver = dataHolder - - data.onEach { - dataHolder.setValue(it) - }.launchIn(scope) - } - - override fun log(priority: Int, message: String) { - logger.log(priority, message) - } - - override fun getMinLogPriority(): Int { - return Log.VERBOSE - } - - override fun getGattCallback(): BleManagerGattCallback { - return HTManagerGattCallback() - } - - private inner class HTManagerGattCallback : BleManagerGattCallback() { - override fun initialize() { - super.initialize() - - setIndicationCallback(htCharacteristic) - .asValidResponseFlow() - .onEach { - data.tryEmit(data.value.copy(temperatureValue = it.temperature)) - }.launchIn(scope) - enableIndications(htCharacteristic).enqueue() - - setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow().onEach { - data.value = data.value.copy(batteryLevel = it.batteryLevel) - }.launchIn(scope) - enableNotifications(batteryLevelCharacteristic).enqueue() - } - - override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - gatt.getService(HTS_SERVICE_UUID)?.run { - htCharacteristic = getCharacteristic(HT_MEASUREMENT_CHARACTERISTIC_UUID) - } - gatt.getService(BATTERY_SERVICE_UUID)?.run { - batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID) - } - return htCharacteristic != null - } - - override fun onServicesInvalidated() { - htCharacteristic = null - batteryLevelCharacteristic = null - } - } -} diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSData.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServicesData.kt similarity index 86% rename from profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSData.kt rename to profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServicesData.kt index 1bfb09f1..05398976 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSData.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServicesData.kt @@ -31,7 +31,11 @@ package no.nordicsemi.android.hts.data -internal data class HTSData( - val temperatureValue: Float = 0f, +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.hts.HTSData + +internal data class HTSServicesData( + val data: HTSData = HTSData(), val batteryLevel: Int? = null, + val connectionState: GattConnectionState? = null ) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index a6b88dd3..7b382b23 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -33,22 +33,18 @@ package no.nordicsemi.android.hts.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.hts.data.HTSData -import no.nordicsemi.android.hts.data.HTSManager +import no.nordicsemi.android.hts.data.HTSServicesData import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.service.IdleResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.hts.HTSData +import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -56,57 +52,40 @@ import javax.inject.Singleton class HTSRepository @Inject constructor( @ApplicationContext private val context: Context, - private val serviceManager: ServiceManager, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst + private val serviceManager: ServiceManager ) { - private var manager: HTSManager? = null private var logger: NordicLogger? = null - private val _data = MutableStateFlow>(IdleResult()) + private val _data = MutableStateFlow(HTSServicesData()) internal val data = _data.asStateFlow() - val isRunning = data.map { it.isRunning() } - val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } + private val _stopEvent = simpleSharedFlow() + internal val stopEvent = _stopEvent.asSharedFlow() + + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(HTSService::class.java, device) } - fun start(device: ServerDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HTS", device.address).also { - logger = it - } - val manager = HTSManager(context, scope, createdLogger) - this.manager = manager + fun onConnectionStateChanged(connectionState: GattConnectionState?) { + _data.value = _data.value.copy(connectionState = connectionState) + } - manager.dataHolder.status.onEach { - _data.value = it - }.launchIn(scope) + fun onHTSDataChanged(data: HTSData) { + _data.value = _data.value.copy(data = data) + } - scope.launch { - manager.start(device) - } + fun onBatteryLevelChanged(batteryLevel: Int) { + _data.value = _data.value.copy(batteryLevel = batteryLevel) } fun openLogger() { NordicLogger.launch(context, logger) } - private suspend fun HTSManager.start(device: ServerDevice) { -// try { -// connect(device.device) -// .useAutoConnect(false) -// .retry(3, 100) -// .suspend() -// } catch (e: Exception) { -// e.printStackTrace() -// } - } - fun release() { - manager?.disconnect()?.enqueue() logger = null - manager = null + _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 05ff5dbb..bd308176 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -31,33 +31,94 @@ package no.nordicsemi.android.hts.repository +import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.hts.HTSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import java.util.* import javax.inject.Inject +val HTS_SERVICE_UUID: UUID = UUID.fromString("00001809-0000-1000-8000-00805f9b34fb") +private val HTS_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") + +@SuppressLint("MissingPermission") @AndroidEntryPoint internal class HTSService : NotificationService() { @Inject lateinit var repository: HTSRepository + private lateinit var client: BleGattClient + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) val device = intent!!.getParcelableExtra(DEVICE_DATA)!! - repository.start(device, lifecycleScope) + startGattClient(device) - repository.hasBeenDisconnected.onEach { - if (it) stopSelf() - }.launchIn(lifecycleScope) + repository.stopEvent + .onEach { disconnect() } + .launchIn(lifecycleScope) return START_REDELIVER_INTENT } + + private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { + client = blinkyDevice.connect(this@HTSService) + + client.connectionState + .onEach { repository.onConnectionStateChanged(it) } + .filterNotNull() + .onEach { stopIfDisconnected(it) } + .launchIn(lifecycleScope) + + client.services + .filterNotNull() + .onEach { configureGatt(it) } + .launchIn(lifecycleScope) + } + + private suspend fun configureGatt(services: BleGattServices) { + val htsService = services.findService(HTS_SERVICE_UUID)!! + val htsMeasurementCharacteristic = htsService.findCharacteristic(HTS_MEASUREMENT_CHARACTERISTIC_UUID)!! + val batteryService = services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + batteryLevelCharacteristic.getNotifications() + .mapNotNull { BatteryLevelParser.parse(it) } + .onEach { repository.onBatteryLevelChanged(it) } + .launchIn(lifecycleScope) + + htsMeasurementCharacteristic.getNotifications() + .mapNotNull { HTSDataParser.parse(it) } + .onEach { repository.onHTSDataChanged(it) } + .launchIn(lifecycleScope) + } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + stopSelf() + } + } + + private fun disconnect() { + client.disconnect() + } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt index 4c2242b6..2b5abc5f 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt @@ -45,14 +45,14 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.view.RadioButtonGroup import no.nordicsemi.android.hts.R -import no.nordicsemi.android.hts.data.HTSData +import no.nordicsemi.android.hts.data.HTSServicesData import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun HTSContentView(state: HTSData, temperatureUnit: TemperatureUnit, onEvent: (HTSScreenViewEvent) -> Unit) { +internal fun HTSContentView(state: HTSServicesData, temperatureUnit: TemperatureUnit, onEvent: (HTSScreenViewEvent) -> Unit) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally @@ -76,7 +76,7 @@ internal fun HTSContentView(state: HTSData, temperatureUnit: TemperatureUnit, on KeyValueField( stringResource(id = R.string.hts_temperature), - displayTemperature(state.temperatureValue, temperatureUnit) + displayTemperature(state.data.temperature, temperatureUnit) ) } @@ -99,5 +99,5 @@ internal fun HTSContentView(state: HTSData, temperatureUnit: TemperatureUnit, on @Preview @Composable private fun Preview() { - HTSContentView(state = HTSData(), TemperatureUnit.CELSIUS) { } + HTSContentView(state = HTSServicesData(), TemperatureUnit.CELSIUS) { } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 60f506cf..9a406c24 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -48,15 +48,7 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.hts.R import no.nordicsemi.android.hts.viewmodel.HTSViewModel -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult -import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -80,15 +72,12 @@ fun HTSScreen() { ) { when (state.htsManagerState) { NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.htsManagerState.result) { - is IdleResult, - is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) } - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) } - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - is SuccessResult -> HTSContentView(state.htsManagerState.result.data, state.temperatureUnit) { viewModel.onEvent(it) } + is WorkingState -> when (state.htsManagerState.result.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> HTSContentView(state.htsManagerState.result, state.temperatureUnit) { viewModel.onEvent(it) } } } } @@ -97,16 +86,11 @@ fun HTSScreen() { @Composable private fun AppBar(state: HTSViewState, navigateUp: () -> Unit, viewModel: HTSViewModel) { - val toolbarName = (state.htsManagerState as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { - BackIconAppBar(stringResource(id = R.string.hts_title), navigateUp) - } else { - LoggerIconAppBar(toolbarName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { + if (state.deviceName?.isNotBlank() == true) { + LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) } + } else { + BackIconAppBar(stringResource(id = R.string.hts_title), navigateUp) } } - diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt index 3e05d629..d954ab67 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt @@ -31,16 +31,16 @@ package no.nordicsemi.android.hts.view -import no.nordicsemi.android.hts.data.HTSData -import no.nordicsemi.android.service.BleManagerResult +import no.nordicsemi.android.hts.data.HTSServicesData internal data class HTSViewState( val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS, - val htsManagerState: HTSManagerState = NoDeviceState + val htsManagerState: HTSManagerState = NoDeviceState, + val deviceName: String? = null ) internal sealed class HTSManagerState -internal data class WorkingState(val result: BleManagerResult) : HTSManagerState() +internal data class WorkingState(val result: HTSServicesData) : HTSManagerState() internal object NoDeviceState : HTSManagerState() diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index d11c8f85..c5a77335 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -46,8 +46,8 @@ import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.hts.data.HTS_SERVICE_UUID import no.nordicsemi.android.hts.repository.HTSRepository +import no.nordicsemi.android.hts.repository.HTS_SERVICE_UUID import no.nordicsemi.android.hts.view.DisconnectEvent import no.nordicsemi.android.hts.view.HTSScreenViewEvent import no.nordicsemi.android.hts.view.HTSViewState @@ -56,7 +56,7 @@ import no.nordicsemi.android.hts.view.OnTemperatureUnitSelected import no.nordicsemi.android.hts.view.OpenLoggerEvent import no.nordicsemi.android.hts.view.WorkingState import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.ConnectedResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -80,7 +80,7 @@ internal class HTSViewModel @Inject constructor( repository.data.onEach { _state.value = _state.value.copy(htsManagerState = WorkingState(it)) - (it as? ConnectedResult)?.let { + if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.HTS)) } }.launchIn(viewModelScope) @@ -97,10 +97,15 @@ internal class HTSViewModel @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) { + _state.value = _state.value.copy(deviceName = device.name) + repository.launch(device) + } + fun onEvent(event: HTSScreenViewEvent) { when (event) { DisconnectEvent -> disconnect() From 84eb61d59ffbfdfe499c76c98e0cf8b865f0037b Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 9 Mar 2023 16:48:12 +0100 Subject: [PATCH 007/101] Migrate HRS profile --- .../{CSCServicesData.kt => CSCServiceData.kt} | 2 +- .../android/csc/repository/CSCRepository.kt | 4 +- .../android/csc/view/CSCContentView.kt | 6 +- .../nordicsemi/android/csc/view/CSCState.kt | 4 +- .../android/csc/view/SensorsReadingView.kt | 6 +- profile_hrs/build.gradle.kts | 1 + .../nordicsemi/android/hrs/data/HRSManager.kt | 135 ------------------ .../data/{HRSData.kt => HRSServiceData.kt} | 14 +- .../android/hrs/service/HRSRepository.kt | 68 ++++----- .../android/hrs/service/HRSService.kt | 75 +++++++++- .../android/hrs/view/HRSContentView.kt | 6 +- .../nordicsemi/android/hrs/view/HRSScreen.kt | 39 ++--- .../nordicsemi/android/hrs/view/HRSState.kt | 18 +-- .../android/hrs/view/LineChartView.kt | 22 +-- .../android/hrs/viewmodel/HRSViewModel.kt | 16 +-- .../{HTSServicesData.kt => HTSServiceData.kt} | 2 +- .../android/hts/repository/HTSRepository.kt | 4 +- .../android/hts/view/HTSContentView.kt | 6 +- .../nordicsemi/android/hts/view/HTSState.kt | 4 +- 19 files changed, 160 insertions(+), 272 deletions(-) rename profile_csc/src/main/java/no/nordicsemi/android/csc/data/{CSCServicesData.kt => CSCServiceData.kt} (91%) delete mode 100644 profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSManager.kt rename profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/{HRSData.kt => HRSServiceData.kt} (82%) rename profile_hts/src/main/java/no/nordicsemi/android/hts/data/{HTSServicesData.kt => HTSServiceData.kt} (98%) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt similarity index 91% rename from profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt rename to profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt index f1ebb66e..70149f3f 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServicesData.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt @@ -3,7 +3,7 @@ package no.nordicsemi.android.csc.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData -data class CSCServicesData( +data class CSCServiceData( val data: CSCData = CSCData(), val batteryLevel: Int? = null, val connectionState: GattConnectionState? = null diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 9f4103df..ad90c346 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.csc.data.CSCServicesData +import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData @@ -61,7 +61,7 @@ class CSCRepository @Inject constructor( private val _wheelSize = MutableStateFlow(WheelSizes.default) internal val wheelSize = _wheelSize.asStateFlow() - private val _data = MutableStateFlow(CSCServicesData()) + private val _data = MutableStateFlow(CSCServiceData()) internal val data = _data.asStateFlow() private val _stopEvent = simpleSharedFlow() diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt index e8133ab8..c2204ab8 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt @@ -49,7 +49,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.view.RadioButtonGroup import no.nordicsemi.android.csc.R -import no.nordicsemi.android.csc.data.CSCServicesData +import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize import no.nordicsemi.android.ui.view.ScreenSection @@ -58,7 +58,7 @@ import no.nordicsemi.android.ui.view.dialog.FlowCanceled import no.nordicsemi.android.ui.view.dialog.ItemSelectedResult @Composable -internal fun CSCContentView(state: CSCServicesData, speedUnit: SpeedUnit, onEvent: (CSCViewEvent) -> Unit) { +internal fun CSCContentView(state: CSCServiceData, speedUnit: SpeedUnit, onEvent: (CSCViewEvent) -> Unit) { val showDialog = rememberSaveable { mutableStateOf(false) } if (showDialog.value) { @@ -125,5 +125,5 @@ private fun SettingsSection( @Preview @Composable private fun ConnectedPreview() { - CSCContentView(CSCServicesData(), SpeedUnit.KM_H) { } + CSCContentView(CSCServiceData(), SpeedUnit.KM_H) { } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt index 9fbbe674..c5638323 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt @@ -31,7 +31,7 @@ package no.nordicsemi.android.csc.view -import no.nordicsemi.android.csc.data.CSCServicesData +import no.nordicsemi.android.csc.data.CSCServiceData internal data class CSCViewState( val speedUnit: SpeedUnit = SpeedUnit.M_S, @@ -41,6 +41,6 @@ internal data class CSCViewState( internal sealed class CSCMangerState -internal data class WorkingState(val result: CSCServicesData) : CSCMangerState() +internal data class WorkingState(val result: CSCServiceData) : CSCMangerState() internal object NoDeviceState : CSCMangerState() diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt index cfa0c373..10cbbd5c 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt @@ -40,14 +40,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.csc.R -import no.nordicsemi.android.csc.data.CSCServicesData +import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun SensorsReadingView(state: CSCServicesData, speedUnit: SpeedUnit) { +internal fun SensorsReadingView(state: CSCServiceData, speedUnit: SpeedUnit) { val csc = state.data ScreenSection { SectionTitle(resId = R.drawable.ic_records, title = "Records") @@ -80,5 +80,5 @@ internal fun SensorsReadingView(state: CSCServicesData, speedUnit: SpeedUnit) { @Preview @Composable private fun Preview() { - SensorsReadingView(CSCServicesData(), SpeedUnit.KM_H) + SensorsReadingView(CSCServiceData(), SpeedUnit.KM_H) } diff --git a/profile_hrs/build.gradle.kts b/profile_hrs/build.gradle.kts index ea8177d8..470c8a69 100644 --- a/profile_hrs/build.gradle.kts +++ b/profile_hrs/build.gradle.kts @@ -56,6 +56,7 @@ dependencies { implementation(libs.nordic.navigation) implementation(libs.nordic.uiscanner) implementation(libs.nordic.uilogger) + implementation(libs.nordic.core) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSManager.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSManager.kt deleted file mode 100644 index d02ab5cc..00000000 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSManager.kt +++ /dev/null @@ -1,135 +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.hrs.data - -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic -import android.content.Context -import android.util.Log -import kotlinx.coroutines.CoroutineScope -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.battery.BatteryLevelResponse -import no.nordicsemi.android.ble.common.callback.hr.BodySensorLocationResponse -import no.nordicsemi.android.ble.common.callback.hr.HeartRateMeasurementResponse -import no.nordicsemi.android.ble.ktx.asValidResponseFlow -import no.nordicsemi.android.ble.ktx.suspendForValidResponse -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.service.ConnectionObserverAdapter -import no.nordicsemi.android.utils.launchWithCatch -import java.util.* - -val HRS_SERVICE_UUID: UUID = UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb") -private val BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID = UUID.fromString("00002A38-0000-1000-8000-00805f9b34fb") -private val HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A37-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 HRSManager( - context: Context, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : BleManager(context) { - - private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null - private var heartRateCharacteristic: BluetoothGattCharacteristic? = null - private var bodySensorLocationCharacteristic: BluetoothGattCharacteristic? = null - - private val data = MutableStateFlow(HRSData()) - val dataHolder = ConnectionObserverAdapter() - - init { - connectionObserver = dataHolder - - data.onEach { - dataHolder.setValue(it) - }.launchIn(scope) - } - - override fun log(priority: Int, message: String) { - logger.log(priority, message) - } - - override fun getMinLogPriority(): Int { - return Log.VERBOSE - } - - override fun getGattCallback(): BleManagerGattCallback { - return HeartRateManagerCallback() - } - - private inner class HeartRateManagerCallback : BleManagerGattCallback() { - override fun initialize() { - super.initialize() - - scope.launchWithCatch { - val readData = readCharacteristic(bodySensorLocationCharacteristic) - .suspendForValidResponse() - - data.value = data.value.copy(sensorLocation = readData.sensorLocation) - } - - setNotificationCallback(heartRateCharacteristic).asValidResponseFlow() - .onEach { - val result = data.value.heartRates.toMutableList().apply { - add(it.heartRate) - } - data.tryEmit(data.value.copy(heartRates = result)) - }.launchIn(scope) - enableNotifications(heartRateCharacteristic).enqueue() - - setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow().onEach { - data.value = data.value.copy(batteryLevel = it.batteryLevel) - }.launchIn(scope) - enableNotifications(batteryLevelCharacteristic).enqueue() - } - - override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - gatt.getService(HRS_SERVICE_UUID)?.run { - heartRateCharacteristic = getCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID) - bodySensorLocationCharacteristic = getCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID) - } - gatt.getService(BATTERY_SERVICE_UUID)?.run { - batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID) - } - return heartRateCharacteristic != null - } - - override fun onServicesInvalidated() { - bodySensorLocationCharacteristic = null - heartRateCharacteristic = null - batteryLevelCharacteristic = null - } - } -} diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSData.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt similarity index 82% rename from profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSData.kt rename to profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt index a72fbc2e..8d326ecd 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSData.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt @@ -31,8 +31,14 @@ package no.nordicsemi.android.hrs.data -internal data class HRSData( - val heartRates: List = emptyList(), +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSData + +internal data class HRSServiceData( + val data: List = emptyList(), + val bodySensorLocation: Int? = null, val batteryLevel: Int? = null, - val sensorLocation: Int = 0, -) + val connectionState: GattConnectionState? = null +) { + val heartRates = data.map { it.heartRate } +} diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 85d0928f..45e49803 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -33,22 +33,18 @@ package no.nordicsemi.android.hrs.service import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.hrs.data.HRSData -import no.nordicsemi.android.hrs.data.HRSManager +import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.service.IdleResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSData +import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -56,57 +52,43 @@ import javax.inject.Singleton class HRSRepository @Inject constructor( @ApplicationContext private val context: Context, - private val serviceManager: ServiceManager, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst + private val serviceManager: ServiceManager ) { - private var manager: HRSManager? = null private var logger: NordicLogger? = null - private val _data = MutableStateFlow>(IdleResult()) + private val _data = MutableStateFlow(HRSServiceData()) internal val data = _data.asStateFlow() - val isRunning = data.map { it.isRunning() } - val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } + private val _stopEvent = simpleSharedFlow() + internal val stopEvent = _stopEvent.asSharedFlow() + + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(HRSService::class.java, device) } - fun start(device: ServerDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HRS", device.address).also { - logger = it - } - val manager = HRSManager(context, scope, createdLogger) - this.manager = manager + fun onConnectionStateChanged(connectionState: GattConnectionState?) { + _data.value = _data.value.copy(connectionState = connectionState) + } - manager.dataHolder.status.onEach { - _data.value = it - }.launchIn(scope) + fun onHRSDataChanged(data: HRSData) { + _data.value = _data.value.copy(data = _data.value.data + data) + } - scope.launch { - manager.start(device) - } + fun onBodySensorLocationChanged(bodySensorLocation: Int) { + _data.value = _data.value.copy(bodySensorLocation = bodySensorLocation) + } + + fun onBatteryLevelChanged(batteryLevel: Int) { + _data.value = _data.value.copy(batteryLevel = batteryLevel) } fun openLogger() { NordicLogger.launch(context, logger) } - private suspend fun HRSManager.start(device: ServerDevice) { -// try { -// connect(device.device) -// .useAutoConnect(false) -// .retry(3, 100) -// .suspend() -// } catch (e: Exception) { -// e.printStackTrace() -// } - } - fun release() { - manager?.disconnect()?.enqueue() - logger = null - manager = null + _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index a3d7675d..e267a7dc 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -31,33 +31,100 @@ package no.nordicsemi.android.hrs.service +import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.hrs.BodySensorLocationParser +import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import java.util.* import javax.inject.Inject +val HRS_SERVICE_UUID: UUID = UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb") +private val BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID = UUID.fromString("00002A38-0000-1000-8000-00805f9b34fb") +private val HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A37-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") + +@SuppressLint("MissingPermission") @AndroidEntryPoint internal class HRSService : NotificationService() { @Inject lateinit var repository: HRSRepository + private lateinit var client: BleGattClient + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) val device = intent!!.getParcelableExtra(DEVICE_DATA)!! - repository.start(device, lifecycleScope) + startGattClient(device) - repository.hasBeenDisconnected.onEach { - if (it) stopSelf() - }.launchIn(lifecycleScope) + repository.stopEvent + .onEach { disconnect() } + .launchIn(lifecycleScope) return START_REDELIVER_INTENT } + + private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { + client = blinkyDevice.connect(this@HRSService) + + client.connectionState + .onEach { repository.onConnectionStateChanged(it) } + .filterNotNull() + .onEach { stopIfDisconnected(it) } + .launchIn(lifecycleScope) + + client.services + .filterNotNull() + .onEach { configureGatt(it) } + .launchIn(lifecycleScope) + } + + private suspend fun configureGatt(services: BleGattServices) { + val htsService = services.findService(HRS_SERVICE_UUID)!! + val htsMeasurementCharacteristic = htsService.findCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID)!! + val bodySensorLocationCharacteristic = htsService.findCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID)!! + val batteryService = services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + val bodySensorLocation = bodySensorLocationCharacteristic.read() + BodySensorLocationParser.parse(bodySensorLocation)?.let { repository.onBodySensorLocationChanged(it) } + + batteryLevelCharacteristic.getNotifications() + .mapNotNull { BatteryLevelParser.parse(it) } + .onEach { repository.onBatteryLevelChanged(it) } + .launchIn(lifecycleScope) + + htsMeasurementCharacteristic.getNotifications() + .mapNotNull { HRSDataParser.parse(it) } + .onEach { repository.onHRSDataChanged(it) } + .launchIn(lifecycleScope) + } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + stopSelf() + } + } + + private fun disconnect() { + client.disconnect() + } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt index d7e96190..3b653850 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt @@ -46,13 +46,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.hrs.R -import no.nordicsemi.android.hrs.data.HRSData +import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun HRSContentView(state: HRSData, zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) { +internal fun HRSContentView(state: HRSServiceData, zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) { Column( horizontalAlignment = Alignment.CenterHorizontally ) { @@ -102,5 +102,5 @@ private fun Menu(zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) { @Preview @Composable private fun Preview() { - HRSContentView(state = HRSData(), zoomIn = false) { } + HRSContentView(state = HRSServiceData(), zoomIn = false) { } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index c615f37a..c37c2882 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -48,15 +48,7 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.hrs.R import no.nordicsemi.android.hrs.viewmodel.HRSViewModel -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult -import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -78,17 +70,14 @@ fun HRSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state) { + when (state.hrsManagerState) { NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.result) { - is IdleResult, - is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) } - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) } - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - is SuccessResult -> HRSContentView(state.result.data, state.zoomIn) { viewModel.onEvent(it) } + is WorkingState -> when (state.hrsManagerState.result.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> HRSContentView(state.hrsManagerState.result, state.zoomIn) { viewModel.onEvent(it) } } } } @@ -97,15 +86,11 @@ fun HRSScreen() { @Composable private fun AppBar(state: HRSViewState, navigateUp: () -> Unit, viewModel: HRSViewModel) { - val toolbarName = (state as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { - BackIconAppBar(stringResource(id = R.string.hrs_title), navigateUp) - } else { - LoggerIconAppBar(toolbarName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { + if (state.deviceName?.isNotBlank() == true) { + LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) } + } else { + BackIconAppBar(stringResource(id = R.string.hrs_title), navigateUp) } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt index b003daa7..d0d3e597 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt @@ -31,14 +31,16 @@ package no.nordicsemi.android.hrs.view -import no.nordicsemi.android.hrs.data.HRSData -import no.nordicsemi.android.service.BleManagerResult +import no.nordicsemi.android.hrs.data.HRSServiceData -internal sealed class HRSViewState - -internal data class WorkingState( - val result: BleManagerResult, +internal data class HRSViewState( val zoomIn: Boolean = false, -) : HRSViewState() + val hrsManagerState: HRSManagerState = NoDeviceState, + val deviceName: String? = null +) -internal object NoDeviceState : HRSViewState() +internal sealed class HRSManagerState + +internal data class WorkingState(val result: HRSServiceData) : HRSManagerState() + +internal object NoDeviceState : HRSManagerState() diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/LineChartView.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/LineChartView.kt index 9f0ad9c4..aa185af2 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/LineChartView.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/LineChartView.kt @@ -47,7 +47,7 @@ import com.github.mikephil.charting.data.Entry import com.github.mikephil.charting.data.LineData import com.github.mikephil.charting.data.LineDataSet import com.github.mikephil.charting.interfaces.datasets.ILineDataSet -import no.nordicsemi.android.hrs.data.HRSData +import no.nordicsemi.android.hrs.data.HRSServiceData private const val X_AXIS_ELEMENTS_COUNT = 40f @@ -55,7 +55,7 @@ private const val AXIS_MIN = 0 private const val AXIS_MAX = 300 @Composable -internal fun LineChartView(state: HRSData, zoomIn: Boolean,) { +internal fun LineChartView(state: HRSServiceData, zoomIn: Boolean,) { val items = state.heartRates.takeLast(X_AXIS_ELEMENTS_COUNT.toInt()).reversed() val isSystemInDarkTheme = isSystemInDarkTheme() AndroidView( @@ -119,8 +119,8 @@ internal fun createLineChartView( val entries = points.mapIndexed { i, v -> Entry(-i.toFloat(), v.toFloat()) }.reversed() - // create a dataset and give it a type + // create a dataset and give it a type if (data != null && data.dataSetCount > 0) { val set1 = data!!.getDataSetByIndex(0) as LineDataSet set1.values = entries @@ -133,13 +133,9 @@ internal fun createLineChartView( set1.setDrawIcons(false) set1.setDrawValues(false) - // draw dashed line - // draw dashed line set1.enableDashedLine(10f, 5f, 0f) - // black lines and points - // black lines and points if (isDarkTheme) { set1.color = Color.WHITE @@ -149,31 +145,21 @@ internal fun createLineChartView( set1.setCircleColor(Color.BLACK) } - // line thickness and point size - // line thickness and point size set1.lineWidth = 1f set1.circleRadius = 3f - // draw points as solid circles - // draw points as solid circles set1.setDrawCircleHole(false) - // customize legend entry - // customize legend entry set1.formLineWidth = 1f set1.formLineDashEffect = DashPathEffect(floatArrayOf(10f, 5f), 0f) set1.formSize = 15f - // text size of values - // text size of values set1.valueTextSize = 9f - // draw selection line as dashed - // draw selection line as dashed set1.enableDashedHighlightLine(10f, 5f, 0f) @@ -183,8 +169,6 @@ internal fun createLineChartView( // create a data object with the data sets val data = LineData(dataSets) - // set data - // set data setData(data) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index 0ebe4367..6e14805b 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -46,18 +46,17 @@ import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.hrs.data.HRS_SERVICE_UUID import no.nordicsemi.android.hrs.service.HRSRepository +import no.nordicsemi.android.hrs.service.HRS_SERVICE_UUID import no.nordicsemi.android.hrs.view.DisconnectEvent import no.nordicsemi.android.hrs.view.HRSScreenViewEvent import no.nordicsemi.android.hrs.view.HRSViewState import no.nordicsemi.android.hrs.view.NavigateUpEvent -import no.nordicsemi.android.hrs.view.NoDeviceState import no.nordicsemi.android.hrs.view.OpenLoggerEvent import no.nordicsemi.android.hrs.view.SwitchZoomEvent import no.nordicsemi.android.hrs.view.WorkingState import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.ConnectedResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -68,7 +67,7 @@ internal class HRSViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(NoDeviceState) + private val _state = MutableStateFlow(HRSViewState()) val state = _state.asStateFlow() init { @@ -79,10 +78,9 @@ internal class HRSViewModel @Inject constructor( } repository.data.onEach { - val zoomIn = (_state.value as? WorkingState)?.zoomIn ?: false - _state.value = WorkingState(it, zoomIn) + _state.value = _state.value.copy(hrsManagerState = WorkingState(it)) - (it as? ConnectedResult)?.let { + if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.HRS)) } }.launchIn(viewModelScope) @@ -113,9 +111,7 @@ internal class HRSViewModel @Inject constructor( } private fun onZoomButtonClicked() { - (_state.value as? WorkingState)?.let { - _state.value = it.copy(zoomIn = !it.zoomIn) - } + _state.value = _state.value.copy(zoomIn = !_state.value.zoomIn) } private fun disconnect() { diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServicesData.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt similarity index 98% rename from profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServicesData.kt rename to profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt index 05398976..ab54a19f 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServicesData.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt @@ -34,7 +34,7 @@ package no.nordicsemi.android.hts.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.hts.HTSData -internal data class HTSServicesData( +internal data class HTSServiceData( val data: HTSData = HTSData(), val batteryLevel: Int? = null, val connectionState: GattConnectionState? = null diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 7b382b23..e3ee177d 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.hts.data.HTSServicesData +import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.hts.HTSData @@ -56,7 +56,7 @@ class HTSRepository @Inject constructor( ) { private var logger: NordicLogger? = null - private val _data = MutableStateFlow(HTSServicesData()) + private val _data = MutableStateFlow(HTSServiceData()) internal val data = _data.asStateFlow() private val _stopEvent = simpleSharedFlow() diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt index 2b5abc5f..d46a4a0f 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt @@ -45,14 +45,14 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.view.RadioButtonGroup import no.nordicsemi.android.hts.R -import no.nordicsemi.android.hts.data.HTSServicesData +import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun HTSContentView(state: HTSServicesData, temperatureUnit: TemperatureUnit, onEvent: (HTSScreenViewEvent) -> Unit) { +internal fun HTSContentView(state: HTSServiceData, temperatureUnit: TemperatureUnit, onEvent: (HTSScreenViewEvent) -> Unit) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally @@ -99,5 +99,5 @@ internal fun HTSContentView(state: HTSServicesData, temperatureUnit: Temperature @Preview @Composable private fun Preview() { - HTSContentView(state = HTSServicesData(), TemperatureUnit.CELSIUS) { } + HTSContentView(state = HTSServiceData(), TemperatureUnit.CELSIUS) { } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt index d954ab67..ef122680 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt @@ -31,7 +31,7 @@ package no.nordicsemi.android.hts.view -import no.nordicsemi.android.hts.data.HTSServicesData +import no.nordicsemi.android.hts.data.HTSServiceData internal data class HTSViewState( val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS, @@ -41,6 +41,6 @@ internal data class HTSViewState( internal sealed class HTSManagerState -internal data class WorkingState(val result: HTSServicesData) : HTSManagerState() +internal data class WorkingState(val result: HTSServiceData) : HTSManagerState() internal object NoDeviceState : HTSManagerState() From e10ebcf4b566404005311f1dd0f59077dcdf186f Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 9 Mar 2023 17:21:44 +0100 Subject: [PATCH 008/101] Add RSCS profile --- .../android/hrs/viewmodel/HRSViewModel.kt | 7 +- profile_rscs/build.gradle.kts | 1 + .../android/rscs/data/RSCSManager.kt | 126 ------------------ .../data/{RSCSData.kt => RSCSServiceData.kt} | 39 +++--- .../android/rscs/repository/RSCSRepository.kt | 63 +++------ .../android/rscs/repository/RSCSService.kt | 69 +++++++++- .../android/rscs/view/RSCSContentView.kt | 6 +- .../android/rscs/view/RSCSScreen.kt | 39 ++---- .../nordicsemi/android/rscs/view/RSCSState.kt | 14 +- .../android/rscs/view/SensorsReadingView.kt | 6 +- .../android/rscs/viewmodel/RSCSViewModel.kt | 18 ++- profile_rscs/src/main/res/values/strings.xml | 6 + 12 files changed, 159 insertions(+), 235 deletions(-) delete mode 100644 profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSManager.kt rename profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/{RSCSData.kt => RSCSServiceData.kt} (65%) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index 6e14805b..d2237b36 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -97,10 +97,15 @@ internal class HRSViewModel @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) { + _state.value = _state.value.copy(deviceName = device.name) + repository.launch(device) + } + fun onEvent(event: HRSScreenViewEvent) { when (event) { DisconnectEvent -> disconnect() diff --git a/profile_rscs/build.gradle.kts b/profile_rscs/build.gradle.kts index 360d8826..513505bc 100644 --- a/profile_rscs/build.gradle.kts +++ b/profile_rscs/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(libs.nordic.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.nordic.core) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSManager.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSManager.kt deleted file mode 100644 index 30668a64..00000000 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSManager.kt +++ /dev/null @@ -1,126 +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.rscs.data - -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic -import android.content.Context -import android.util.Log -import kotlinx.coroutines.CoroutineScope -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.battery.BatteryLevelResponse -import no.nordicsemi.android.ble.common.callback.rsc.RunningSpeedAndCadenceMeasurementResponse -import no.nordicsemi.android.ble.ktx.asValidResponseFlow -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.service.ConnectionObserverAdapter -import java.util.* - -val RSCS_SERVICE_UUID: UUID = UUID.fromString("00001814-0000-1000-8000-00805F9B34FB") -private val RSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A53-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 RSCSManager internal constructor( - context: Context, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : BleManager(context) { - - private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null - private var rscMeasurementCharacteristic: BluetoothGattCharacteristic? = null - - private val data = MutableStateFlow(RSCSData()) - val dataHolder = ConnectionObserverAdapter() - - init { - connectionObserver = dataHolder - - data.onEach { - dataHolder.setValue(it) - }.launchIn(scope) - } - - override fun log(priority: Int, message: String) { - logger.log(priority, message) - } - - override fun getMinLogPriority(): Int { - return Log.VERBOSE - } - - private inner class RSCManagerGattCallback : BleManagerGattCallback() { - - override fun initialize() { - super.initialize() - setNotificationCallback(rscMeasurementCharacteristic).asValidResponseFlow() - .onEach { - data.tryEmit(data.value.copy( - running = it.isRunning, - instantaneousCadence = it.instantaneousCadence, - instantaneousSpeed = it.instantaneousSpeed, - strideLength = it.strideLength, - totalDistance = it.totalDistance - )) - }.launchIn(scope) - enableNotifications(rscMeasurementCharacteristic).enqueue() - - setNotificationCallback(batteryLevelCharacteristic) - .asValidResponseFlow() - .onEach { - data.value = data.value.copy(batteryLevel = it.batteryLevel) - }.launchIn(scope) - enableNotifications(batteryLevelCharacteristic).enqueue() - } - - public override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - gatt.getService(RSCS_SERVICE_UUID)?.run { - rscMeasurementCharacteristic = getCharacteristic(RSC_MEASUREMENT_CHARACTERISTIC_UUID) - } - gatt.getService(BATTERY_SERVICE_UUID)?.run { - batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID) - } - return rscMeasurementCharacteristic != null - } - - override fun onServicesInvalidated() { - rscMeasurementCharacteristic = null - batteryLevelCharacteristic = null - } - } - - override fun getGattCallback(): BleManagerGattCallback { - return RSCManagerGattCallback() - } -} diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSData.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt similarity index 65% rename from profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSData.kt rename to profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt index 75f303ea..8c2578a0 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSData.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt @@ -31,37 +31,42 @@ package no.nordicsemi.android.rscs.data -internal data class RSCSData( - val batteryLevel: Int? = null, - val running: Boolean = false, - val instantaneousSpeed: Float = 1.0f, - val instantaneousCadence: Int = 0, - val strideLength: Int? = null, - val totalDistance: Long? = null -) { +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSData +import no.nordicsemi.android.rscs.R +internal data class RSCSServiceData( + val data: RSCSData = RSCSData(), + val batteryLevel: Int? = null, + val connectionState: GattConnectionState? = null +) { + @Composable fun displayActivity(): String { - return if (running) { - "Running" + return if (data.running) { + stringResource(id = R.string.rscs_running) } else { - "Walking" + stringResource(id = R.string.rscs_walking) } } + @Composable fun displayPace(): String { - return "$instantaneousCadence min/km" + return stringResource(id = R.string.rscs_speed, data.instantaneousSpeed) } - + @Composable fun displayCadence(): String { - return "$instantaneousCadence RPM" + return stringResource(id = R.string.rscs_rpm, data.instantaneousCadence) } + @Composable fun displayNumberOfSteps(): String? { - if (totalDistance == null || strideLength == null) { + if (data.totalDistance == null || data.strideLength == null) { return null } - val numberOfSteps = totalDistance/strideLength - return "Number of Steps $numberOfSteps" + val numberOfSteps = data.totalDistance!! / data.strideLength!!.toLong() + return stringResource(id = R.string.rscs_steps, numberOfSteps) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 1e270afb..5490d379 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -33,22 +33,18 @@ package no.nordicsemi.android.rscs.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.rscs.data.RSCSData -import no.nordicsemi.android.rscs.data.RSCSManager -import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.service.IdleResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSData +import no.nordicsemi.android.rscs.data.RSCSServiceData +import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -56,57 +52,40 @@ import javax.inject.Singleton class RSCSRepository @Inject constructor( @ApplicationContext private val context: Context, - private val serviceManager: ServiceManager, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst + private val serviceManager: ServiceManager ) { - private var manager: RSCSManager? = null private var logger: NordicLogger? = null - private val _data = MutableStateFlow>(IdleResult()) + private val _data = MutableStateFlow(RSCSServiceData()) internal val data = _data.asStateFlow() - val isRunning = data.map { it.isRunning() } - val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } + private val _stopEvent = simpleSharedFlow() + internal val stopEvent = _stopEvent.asSharedFlow() + + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(RSCSService::class.java, device) } - fun start(device: ServerDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "RSCS", device.address).also { - logger = it - } - val manager = RSCSManager(context, scope, createdLogger) - this.manager = manager + fun onConnectionStateChanged(connectionState: GattConnectionState?) { + _data.value = _data.value.copy(connectionState = connectionState) + } - manager.dataHolder.status.onEach { - _data.value = it - }.launchIn(scope) + fun onRSCSDataChanged(data: RSCSData) { + _data.value = _data.value.copy(data = data) + } - scope.launch { - manager.start(device) - } + fun onBatteryLevelChanged(batteryLevel: Int) { + _data.value = _data.value.copy(batteryLevel = batteryLevel) } fun openLogger() { NordicLogger.launch(context, logger) } - private suspend fun RSCSManager.start(device: ServerDevice) { -// try { -// connect(device.device) -// .useAutoConnect(false) -// .retry(3, 100) -// .suspend() -// } catch (e: Exception) { -// e.printStackTrace() -// } - } - fun release() { - manager?.disconnect()?.enqueue() - manager = null logger = null + _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index acdb6120..f1236945 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -31,33 +31,94 @@ package no.nordicsemi.android.rscs.repository +import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import java.util.* import javax.inject.Inject +val RSCS_SERVICE_UUID: UUID = UUID.fromString("00001814-0000-1000-8000-00805F9B34FB") +private val RSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A53-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") + +@SuppressLint("MissingPermission") @AndroidEntryPoint internal class RSCSService : NotificationService() { @Inject lateinit var repository: RSCSRepository + private lateinit var client: BleGattClient + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) val device = intent!!.getParcelableExtra(DEVICE_DATA)!! - repository.start(device, lifecycleScope) + startGattClient(device) - repository.hasBeenDisconnected.onEach { - if (it) stopSelf() - }.launchIn(lifecycleScope) + repository.stopEvent + .onEach { disconnect() } + .launchIn(lifecycleScope) return START_REDELIVER_INTENT } + + private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { + client = blinkyDevice.connect(this@RSCSService) + + client.connectionState + .onEach { repository.onConnectionStateChanged(it) } + .filterNotNull() + .onEach { stopIfDisconnected(it) } + .launchIn(lifecycleScope) + + client.services + .filterNotNull() + .onEach { configureGatt(it) } + .launchIn(lifecycleScope) + } + + private suspend fun configureGatt(services: BleGattServices) { + val rscsService = services.findService(RSCS_SERVICE_UUID)!! + val rscsMeasurementCharacteristic = rscsService.findCharacteristic(RSC_MEASUREMENT_CHARACTERISTIC_UUID)!! + val batteryService = services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + batteryLevelCharacteristic.getNotifications() + .mapNotNull { BatteryLevelParser.parse(it) } + .onEach { repository.onBatteryLevelChanged(it) } + .launchIn(lifecycleScope) + + rscsMeasurementCharacteristic.getNotifications() + .mapNotNull { RSCSDataParser.parse(it) } + .onEach { repository.onRSCSDataChanged(it) } + .launchIn(lifecycleScope) + } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + stopSelf() + } + } + + private fun disconnect() { + client.disconnect() + } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSContentView.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSContentView.kt index 785429c7..f423ebaa 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSContentView.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSContentView.kt @@ -43,11 +43,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.rscs.R -import no.nordicsemi.android.rscs.data.RSCSData +import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.ui.view.BatteryLevelView @Composable -internal fun RSCSContentView(state: RSCSData, onEvent: (RSCScreenViewEvent) -> Unit) { +internal fun RSCSContentView(state: RSCSServiceData, onEvent: (RSCScreenViewEvent) -> Unit) { Column( horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -74,5 +74,5 @@ internal fun RSCSContentView(state: RSCSData, onEvent: (RSCScreenViewEvent) -> U @Preview @Composable private fun RSCSContentViewPreview() { - RSCSContentView(RSCSData()) { } + RSCSContentView(RSCSServiceData()) { } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index 9825bf92..21beeced 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -46,17 +46,9 @@ import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.rscs.R import no.nordicsemi.android.rscs.viewmodel.RSCSViewModel -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult -import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -78,17 +70,14 @@ fun RSCSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state) { + when (state.rscsManagerState) { NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.result) { - is IdleResult, - is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) } - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) } - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - is SuccessResult -> RSCSContentView(state.result.data) { viewModel.onEvent(it) } + is WorkingState -> when (state.rscsManagerState.result.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> RSCSContentView(state.rscsManagerState.result) { viewModel.onEvent(it) } } } } @@ -97,15 +86,11 @@ fun RSCSScreen() { @Composable private fun AppBar(state: RSCSViewState, navigateUp: () -> Unit, viewModel: RSCSViewModel) { - val toolbarName = (state as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { - BackIconAppBar(stringResource(id = R.string.rscs_title), navigateUp) - } else { - LoggerIconAppBar(toolbarName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { + if (state.deviceName?.isNotBlank() == true) { + LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) } + } else { + BackIconAppBar(stringResource(id = R.string.rscs_title), navigateUp) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt index 23084a89..56710f1f 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt @@ -31,11 +31,15 @@ package no.nordicsemi.android.rscs.view -import no.nordicsemi.android.rscs.data.RSCSData -import no.nordicsemi.android.service.BleManagerResult +import no.nordicsemi.android.rscs.data.RSCSServiceData -internal sealed class RSCSViewState +internal data class RSCSViewState( + val rscsManagerState: RSCSManagerState = NoDeviceState, + val deviceName: String? = null +) -internal data class WorkingState(val result: BleManagerResult) : RSCSViewState() +internal sealed class RSCSManagerState -internal object NoDeviceState : RSCSViewState() +internal data class WorkingState(val result: RSCSServiceData) : RSCSManagerState() + +internal object NoDeviceState : RSCSManagerState() diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt index 241c1c1e..54360961 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt @@ -39,13 +39,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.rscs.R -import no.nordicsemi.android.rscs.data.RSCSData +import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun SensorsReadingView(state: RSCSData) { +internal fun SensorsReadingView(state: RSCSServiceData) { ScreenSection { SectionTitle(resId = R.drawable.ic_records, title = "Records") @@ -66,5 +66,5 @@ internal fun SensorsReadingView(state: RSCSData) { @Preview @Composable private fun Preview() { - SensorsReadingView(RSCSData()) + SensorsReadingView(RSCSServiceData()) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 78360059..3ac505e8 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -47,16 +47,15 @@ import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.rscs.data.RSCS_SERVICE_UUID +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.rscs.repository.RSCSRepository +import no.nordicsemi.android.rscs.repository.RSCS_SERVICE_UUID import no.nordicsemi.android.rscs.view.DisconnectEvent import no.nordicsemi.android.rscs.view.NavigateUpEvent -import no.nordicsemi.android.rscs.view.NoDeviceState import no.nordicsemi.android.rscs.view.OpenLoggerEvent import no.nordicsemi.android.rscs.view.RSCSViewState import no.nordicsemi.android.rscs.view.RSCScreenViewEvent import no.nordicsemi.android.rscs.view.WorkingState -import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -67,7 +66,7 @@ internal class RSCSViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(NoDeviceState) + private val _state = MutableStateFlow(RSCSViewState()) val state = _state.asStateFlow() init { @@ -78,9 +77,9 @@ internal class RSCSViewModel @Inject constructor( } repository.data.onEach { - _state.value = WorkingState(it) + _state.value = _state.value.copy(rscsManagerState = WorkingState(it)) - (it as? ConnectedResult)?.let { + if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.RSCS)) } }.launchIn(viewModelScope) @@ -97,10 +96,15 @@ internal class RSCSViewModel @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) { + _state.value = _state.value.copy(deviceName = device.name) + repository.launch(device) + } + fun onEvent(event: RSCScreenViewEvent) { when (event) { DisconnectEvent -> disconnect() diff --git a/profile_rscs/src/main/res/values/strings.xml b/profile_rscs/src/main/res/values/strings.xml index 35cf1e57..eb061fc4 100644 --- a/profile_rscs/src/main/res/values/strings.xml +++ b/profile_rscs/src/main/res/values/strings.xml @@ -37,4 +37,10 @@ Pace Cadence Number of steps + + Walking + Running + %.1f min/km + %d RPM + Number of Steps %d From 205b94cfa33460a6b352969d1a32799a67bcf8fe Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 9 Mar 2023 17:28:33 +0100 Subject: [PATCH 009/101] Fix not working filtering on scanner screen --- .../no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt index 5fb80fb9..d694e116 100644 --- a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt +++ b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt @@ -19,7 +19,7 @@ val ScannerDestination = defineDestination(ScannerDestinationId) { val arg = navigationViewModel.parameterOf(ScannerDestinationId) ScannerScreen( - uuid = ParcelUuid(UUID.fromString("00001816-0000-1000-8000-00805f9b34fb")), + uuid = arg, onResult = { when (it) { is DeviceSelected -> navigationViewModel.navigateUpWithResult(ScannerDestinationId, it.device) From e448d2c1a4b09b6633abde15844bb12486d34052 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 10 Mar 2023 15:48:01 +0100 Subject: [PATCH 010/101] Migrate BPS profile to the new BLE library --- .../no/nordicsemi/android/bps/data/BPSData.kt | 48 ------- .../nordicsemi/android/bps/data/BPSManager.kt | 133 ------------------ .../android/bps/data/BPSServiceData.kt | 12 ++ .../nordicsemi/android/bps/data/DataMapper.kt | 62 -------- .../android/bps/repository/BPSRepository.kt | 97 ------------- .../android/bps/view/BPSContentView.kt | 4 +- .../nordicsemi/android/bps/view/BPSMapper.kt | 57 -------- .../nordicsemi/android/bps/view/BPSScreen.kt | 38 ++--- .../android/bps/view/BPSSensorsReadingView.kt | 60 ++++++-- .../android/bps/view/BPSViewState.kt | 14 +- .../android/bps/viewmodel/BPSViewModel.kt | 116 ++++++++++++--- profile_bps/src/main/res/values/strings.xml | 2 + 12 files changed, 183 insertions(+), 460 deletions(-) delete mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSData.kt delete mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSManager.kt create mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt delete mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/data/DataMapper.kt delete mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSRepository.kt delete mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSMapper.kt diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSData.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSData.kt deleted file mode 100644 index a8a8ebb0..00000000 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSData.kt +++ /dev/null @@ -1,48 +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.bps.data - -import no.nordicsemi.android.ble.common.profile.bp.BloodPressureTypes -import java.util.* - -data class BPSData( - val batteryLevel: Int? = null, - val cuffPressure: Float = 0f, - val unit: Int = 0, - val pulseRate: Float? = null, - val userID: Int? = null, - val status: BloodPressureTypes.BPMStatus? = null, - val calendar: Calendar? = null, - val systolic: Float = 0f, - val diastolic: Float = 0f, - val meanArterialPressure: Float = 0f, -) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSManager.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSManager.kt deleted file mode 100644 index 93b3a8ca..00000000 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSManager.kt +++ /dev/null @@ -1,133 +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.bps.data - -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic -import android.content.Context -import android.util.Log -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -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.battery.BatteryLevelResponse -import no.nordicsemi.android.ble.common.callback.bps.BloodPressureMeasurementResponse -import no.nordicsemi.android.ble.common.callback.bps.IntermediateCuffPressureResponse -import no.nordicsemi.android.ble.ktx.asValidResponseFlow -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.service.ConnectionObserverAdapter -import java.util.* - -val BPS_SERVICE_UUID: UUID = UUID.fromString("00001810-0000-1000-8000-00805f9b34fb") -private val BPM_CHARACTERISTIC_UUID = UUID.fromString("00002A35-0000-1000-8000-00805f9b34fb") -private val ICP_CHARACTERISTIC_UUID = UUID.fromString("00002A36-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 BPSManager( - @ApplicationContext context: Context, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : BleManager(context) { - - private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null - private var bpmCharacteristic: BluetoothGattCharacteristic? = null - private var icpCharacteristic: BluetoothGattCharacteristic? = null - - private val data = MutableStateFlow(BPSData()) - val dataHolder = ConnectionObserverAdapter() - - init { - connectionObserver = dataHolder - - data.onEach { - dataHolder.setValue(it) - }.launchIn(scope) - } - - override fun log(priority: Int, message: String) { - logger.log(priority, message) - } - - override fun getMinLogPriority(): Int { - return Log.VERBOSE - } - - override fun getGattCallback(): BleManagerGattCallback { - return BloodPressureManagerGattCallback() - } - - private inner class BloodPressureManagerGattCallback : BleManagerGattCallback() { - - @OptIn(ExperimentalCoroutinesApi::class) - override fun initialize() { - super.initialize() - - setNotificationCallback(icpCharacteristic).asValidResponseFlow() - .onEach { data.tryEmit(data.value.copyWithNewResponse(it)) } - .launchIn(scope) - - setIndicationCallback(bpmCharacteristic).asValidResponseFlow() - .onEach { data.tryEmit(data.value.copyWithNewResponse(it)) } - .launchIn(scope) - - setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow() - .onEach { - data.value = data.value.copy(batteryLevel = it.batteryLevel) - }.launchIn(scope) - - enableNotifications(icpCharacteristic).enqueue() - enableIndications(bpmCharacteristic).enqueue() - enableNotifications(batteryLevelCharacteristic).enqueue() - } - - override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - gatt.getService(BPS_SERVICE_UUID)?.run { - bpmCharacteristic = getCharacteristic(BPM_CHARACTERISTIC_UUID) - icpCharacteristic = getCharacteristic(ICP_CHARACTERISTIC_UUID) - } - gatt.getService(BATTERY_SERVICE_UUID)?.run { - batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID) - } - return bpmCharacteristic != null - } - - override fun onServicesInvalidated() { - icpCharacteristic = null - bpmCharacteristic = null - batteryLevelCharacteristic = null - } - } -} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt new file mode 100644 index 00000000..668da28e --- /dev/null +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt @@ -0,0 +1,12 @@ +package no.nordicsemi.android.bps.data + +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementData +import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureData + +data class BPSServiceData ( + val bloodPressureMeasurement: BloodPressureMeasurementData? = null, + val intermediateCuffPressure: IntermediateCuffPressureData? = null, + val batteryLevel: Int? = null, + val connectionState: GattConnectionState? = null +) \ No newline at end of file diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/DataMapper.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/DataMapper.kt deleted file mode 100644 index 5efd28b0..00000000 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/DataMapper.kt +++ /dev/null @@ -1,62 +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.bps.data - -import no.nordicsemi.android.ble.common.callback.bps.BloodPressureMeasurementResponse -import no.nordicsemi.android.ble.common.callback.bps.IntermediateCuffPressureResponse - -internal fun BPSData.copyWithNewResponse(response: IntermediateCuffPressureResponse): BPSData { - return with (response) { - copy( - cuffPressure = cuffPressure, - unit = unit, - pulseRate = pulseRate, - userID = userID, - status = status, - calendar = timestamp - ) - } -} - -internal fun BPSData.copyWithNewResponse(response: BloodPressureMeasurementResponse): BPSData { - return with (response) { - copy( - systolic = systolic, - diastolic = diastolic, - meanArterialPressure = meanArterialPressure, - unit = unit, - pulseRate = pulseRate, - userID = userID, - status = status, - ) - } -} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSRepository.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSRepository.kt deleted file mode 100644 index aeaaf140..00000000 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSRepository.kt +++ /dev/null @@ -1,97 +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.bps.repository - -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.android.scopes.ViewModelScoped -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import no.nordicsemi.android.bps.data.BPSData -import no.nordicsemi.android.bps.data.BPSManager -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.ui.view.StringConst -import javax.inject.Inject - -@ViewModelScoped -internal class BPSRepository @Inject constructor( - @ApplicationContext - private val context: Context, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst -) { - - private var logger: NordicLogger? = null - - fun downloadData(scope: CoroutineScope, device: ServerDevice): Flow> = callbackFlow { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "BPS", device.address).also { - logger = it - } - val manager = BPSManager(context, scope, createdLogger) - - manager.dataHolder.status.onEach { - trySend(it) - }.launchIn(scope) - - scope.launch { - manager.start(device) - } - - awaitClose { - manager.disconnect().enqueue() - logger = null - } - } - - private suspend fun BPSManager.start(device: ServerDevice) { -// try { -// connect(device.device) -// .useAutoConnect(false) -// .retry(3, 100) -// .suspend() -// } catch (e: Exception) { -// e.printStackTrace() -// } - } - - fun openLogger() { - NordicLogger.launch(context, logger) - } -} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSContentView.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSContentView.kt index da2621de..33bc2f87 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSContentView.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSContentView.kt @@ -42,10 +42,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import no.nordicsemi.android.bps.R -import no.nordicsemi.android.bps.data.BPSData +import no.nordicsemi.android.bps.data.BPSServiceData @Composable -internal fun BPSContentView(state: BPSData, onEvent: (BPSViewEvent) -> Unit) { +internal fun BPSContentView(state: BPSServiceData, onEvent: (BPSViewEvent) -> Unit) { Column( horizontalAlignment = Alignment.CenterHorizontally ) { diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSMapper.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSMapper.kt deleted file mode 100644 index c4cecf58..00000000 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSMapper.kt +++ /dev/null @@ -1,57 +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.bps.view - -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import no.nordicsemi.android.bps.R -import no.nordicsemi.android.bps.data.BPSData - -@Composable -fun BPSData.displaySystolic(): String { - return stringResource(id = R.string.bps_blood_pressure, systolic) -} - -@Composable -fun BPSData.displayDiastolic(): String { - return stringResource(id = R.string.bps_blood_pressure, diastolic) -} - -@Composable -fun BPSData.displayMeanArterialPressure(): String { - return stringResource(id = R.string.bps_blood_pressure, meanArterialPressure) -} - -@Composable -fun BPSData.displayHeartRate(): String? { - return pulseRate?.toString() -} \ No newline at end of file diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 939d6322..5f21341b 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -48,15 +48,7 @@ import no.nordicsemi.android.bps.viewmodel.BPSViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult -import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -78,17 +70,15 @@ fun BPSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state) { - NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.result) { - is IdleResult, - is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) } - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) } - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - is SuccessResult -> BPSContentView(state.result.data) { viewModel.onEvent(it) } + if (state.deviceName == null) { + DeviceConnectingView() + } else { + when (state.result.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> BPSContentView(state.result) { viewModel.onEvent(it) } } } } @@ -97,14 +87,10 @@ fun BPSScreen() { @Composable private fun AppBar(state: BPSViewState, navigateUp: () -> Unit, viewModel: BPSViewModel) { - val toolbarName = (state as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { + if (state.deviceName == null) { BackIconAppBar(stringResource(id = R.string.bps_title), navigateUp) } else { - LoggerIconAppBar(toolbarName, { + LoggerIconAppBar(state.deviceName, { viewModel.onEvent(DisconnectEvent) }, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSSensorsReadingView.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSSensorsReadingView.kt index e8789407..2911be68 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSSensorsReadingView.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSSensorsReadingView.kt @@ -34,34 +34,45 @@ package no.nordicsemi.android.bps.view import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.bps.R -import no.nordicsemi.android.bps.data.BPSData +import no.nordicsemi.android.bps.data.BPSServiceData +import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementData +import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureData import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun BPSSensorsReadingView(state: BPSData) { +internal fun BPSSensorsReadingView(state: BPSServiceData) { ScreenSection { Column { SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.bps_records)) - Spacer(modifier = Modifier.height(16.dp)) - KeyValueField(stringResource(id = R.string.bps_systolic), state.displaySystolic()) - Spacer(modifier = Modifier.height(4.dp)) - KeyValueField(stringResource(id = R.string.bps_diastolic), state.displayDiastolic()) - Spacer(modifier = Modifier.height(4.dp)) - KeyValueField(stringResource(id = R.string.bps_mean), state.displayMeanArterialPressure()) - state.displayHeartRate()?.let { + state.bloodPressureMeasurement?.let { + Spacer(modifier = Modifier.height(16.dp)) + BloodPressureView(it) + } + + state.intermediateCuffPressure?.displayHeartRate()?.let { Spacer(modifier = Modifier.height(4.dp)) KeyValueField(stringResource(id = R.string.bps_pulse), it) } + + if (state.intermediateCuffPressure == null && state.bloodPressureMeasurement == null) { + Spacer(modifier = Modifier.height(16.dp)) + Text( + stringResource(id = R.string.no_data_info), + style = MaterialTheme.typography.bodyMedium + ) + } } } @@ -72,8 +83,37 @@ internal fun BPSSensorsReadingView(state: BPSData) { } } +@Composable +private fun BloodPressureView(state: BloodPressureMeasurementData) { + KeyValueField(stringResource(id = R.string.bps_systolic), state.displaySystolic()) + Spacer(modifier = Modifier.height(4.dp)) + KeyValueField(stringResource(id = R.string.bps_diastolic), state.displayDiastolic()) + Spacer(modifier = Modifier.height(4.dp)) + KeyValueField(stringResource(id = R.string.bps_mean), state.displayMeanArterialPressure()) +} + +@Composable +fun BloodPressureMeasurementData.displaySystolic(): String { + return stringResource(id = R.string.bps_blood_pressure, systolic) +} + +@Composable +fun BloodPressureMeasurementData.displayDiastolic(): String { + return stringResource(id = R.string.bps_blood_pressure, diastolic) +} + +@Composable +fun BloodPressureMeasurementData.displayMeanArterialPressure(): String { + return stringResource(id = R.string.bps_blood_pressure, meanArterialPressure) +} + +@Composable +fun IntermediateCuffPressureData.displayHeartRate(): String? { + return pulseRate?.toString() +} + @Preview @Composable private fun Preview() { - BPSSensorsReadingView(BPSData()) + BPSSensorsReadingView(BPSServiceData()) } diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSViewState.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSViewState.kt index 8b51b307..23c10d02 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSViewState.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSViewState.kt @@ -31,13 +31,9 @@ package no.nordicsemi.android.bps.view -import no.nordicsemi.android.bps.data.BPSData -import no.nordicsemi.android.service.BleManagerResult +import no.nordicsemi.android.bps.data.BPSServiceData -internal sealed class BPSViewState - -internal data class WorkingState( - val result: BleManagerResult -) : BPSViewState() - -internal object NoDeviceState : BPSViewState() +internal data class BPSViewState( + val result: BPSServiceData = BPSServiceData(), + val deviceName: String? = null +) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index de180339..d51294ca 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -31,42 +31,63 @@ package no.nordicsemi.android.bps.viewmodel +import android.annotation.SuppressLint +import android.content.Context import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent -import no.nordicsemi.android.bps.data.BPS_SERVICE_UUID -import no.nordicsemi.android.bps.repository.BPSRepository import no.nordicsemi.android.bps.view.BPSViewEvent import no.nordicsemi.android.bps.view.BPSViewState import no.nordicsemi.android.bps.view.DisconnectEvent -import no.nordicsemi.android.bps.view.NoDeviceState import no.nordicsemi.android.bps.view.OpenLoggerEvent -import no.nordicsemi.android.bps.view.WorkingState import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.ConnectedResult +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementData +import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementParser +import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureData +import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureParser import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId +import java.util.* import javax.inject.Inject +val BPS_SERVICE_UUID: UUID = UUID.fromString("00001810-0000-1000-8000-00805f9b34fb") +private val BPM_CHARACTERISTIC_UUID = UUID.fromString("00002A35-0000-1000-8000-00805f9b34fb") +private val ICP_CHARACTERISTIC_UUID = UUID.fromString("00002A36-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") + +@SuppressLint("MissingPermission", "StaticFieldLeak") @HiltViewModel internal class BPSViewModel @Inject constructor( - private val repository: BPSRepository, + @ApplicationContext + private val context: Context, private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(NoDeviceState) + private val _state = MutableStateFlow(BPSViewState()) val state = _state.asStateFlow() + private lateinit var client: BleGattClient + init { navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(BPS_SERVICE_UUID)) @@ -78,24 +99,87 @@ internal class BPSViewModel @Inject constructor( private fun handleArgs(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() - is NavigationResult.Success -> connectDevice(result.value) + is NavigationResult.Success -> startGattClient(result.value) } } fun onEvent(event: BPSViewEvent) { when (event) { DisconnectEvent -> navigationManager.navigateUp() - OpenLoggerEvent -> repository.openLogger() + OpenLoggerEvent -> TODO() } } - private fun connectDevice(device: ServerDevice) { - repository.downloadData(viewModelScope, device).onEach { - _state.value = WorkingState(it) + private fun startGattClient(blinkyDevice: ServerDevice) = viewModelScope.launch { + _state.value = _state.value.copy(deviceName = blinkyDevice.name) - (it as? ConnectedResult)?.let { - analytics.logEvent(ProfileConnectedEvent(Profile.BPS)) - } - }.launchIn(viewModelScope) + client = blinkyDevice.connect(context) + + client.connectionState + .filterNotNull() + .onEach { onDataUpdate(it) } + .onEach { stopIfDisconnected(it) } + .onEach { logAnalytics(it) } + .launchIn(viewModelScope) + + client.services + .filterNotNull() + .onEach { configureGatt(it) } + .launchIn(viewModelScope) + } + + private suspend fun configureGatt(services: BleGattServices) { + val bpsService = services.findService(BPS_SERVICE_UUID)!! + val bpmCharacteristic = bpsService.findCharacteristic(BPM_CHARACTERISTIC_UUID)!! + val icpCharacteristic = bpsService.findCharacteristic(ICP_CHARACTERISTIC_UUID) + val batteryService = services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + batteryLevelCharacteristic.getNotifications() + .mapNotNull { BatteryLevelParser.parse(it) } + .onEach { onDataUpdate(it) } + .launchIn(viewModelScope) + + bpmCharacteristic.getNotifications() + .mapNotNull { BloodPressureMeasurementParser.parse(it) } + .onEach { onDataUpdate(it) } + .launchIn(viewModelScope) + + icpCharacteristic?.getNotifications() + ?.mapNotNull { IntermediateCuffPressureParser.parse(it) } + ?.onEach { onDataUpdate(it) } + ?.launchIn(viewModelScope) + } + + private fun onDataUpdate(connectionState: GattConnectionState) { + val newResult = _state.value.result.copy(connectionState = connectionState) + _state.value = _state.value.copy(result = newResult) + } + + private fun onDataUpdate(batteryLevel: Int) { + val newResult = _state.value.result.copy(batteryLevel = batteryLevel) + _state.value = _state.value.copy(result = newResult) + } + + private fun onDataUpdate(data: BloodPressureMeasurementData) { + val newResult = _state.value.result.copy(bloodPressureMeasurement = data) + _state.value = _state.value.copy(result = newResult) + } + + private fun onDataUpdate(data: IntermediateCuffPressureData) { + val newResult = _state.value.result.copy(intermediateCuffPressure = data) + _state.value = _state.value.copy(result = newResult) + } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + navigationManager.navigateUp() + } + } + + private fun logAnalytics(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_CONNECTED) { + analytics.logEvent(ProfileConnectedEvent(Profile.BPS)) + } } } diff --git a/profile_bps/src/main/res/values/strings.xml b/profile_bps/src/main/res/values/strings.xml index e465ae40..524087c9 100644 --- a/profile_bps/src/main/res/values/strings.xml +++ b/profile_bps/src/main/res/values/strings.xml @@ -35,6 +35,8 @@ Data + No data available. If you are using nRF DK\'s press button 1 to see the result. + Systolic Diastolic Mean AP From 3c3f1b2c8ba6257434c6db3bf0e6f433bacccf7c Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 13 Mar 2023 12:38:08 +0100 Subject: [PATCH 011/101] Migrate GLS profile to the new BLE library --- .../java/no/nordicsemi/android/bps/data/BPSServiceData.kt | 4 ++-- .../no/nordicsemi/android/bps/view/BPSSensorsReadingView.kt | 4 ++-- .../no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt | 4 ++-- .../java/no/nordicsemi/android/csc/data/CSCServiceData.kt | 2 +- .../no/nordicsemi/android/csc/repository/CSCRepository.kt | 6 +++--- .../java/no/nordicsemi/android/csc/view/CSCContentView.kt | 4 ++-- .../main/java/no/nordicsemi/android/csc/view/CSCMappers.kt | 2 +- .../java/no/nordicsemi/android/csc/view/CSCViewEvent.kt | 2 +- .../java/no/nordicsemi/android/csc/view/WheelSizeView.kt | 2 +- .../main/java/no/nordicsemi/android/gls/data/GLSManager.kt | 3 +-- .../java/no/nordicsemi/android/hrs/data/HRSServiceData.kt | 2 +- .../java/no/nordicsemi/android/hrs/service/HRSRepository.kt | 2 +- .../java/no/nordicsemi/android/hts/data/HTSServiceData.kt | 2 +- .../no/nordicsemi/android/hts/repository/HTSRepository.kt | 2 +- .../java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt | 2 +- .../no/nordicsemi/android/rscs/repository/RSCSRepository.kt | 2 +- 16 files changed, 22 insertions(+), 23 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt index 668da28e..b1608080 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt @@ -1,8 +1,8 @@ package no.nordicsemi.android.bps.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementData -import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureData +import no.nordicsemi.android.kotlin.ble.profile.bps.data.BloodPressureMeasurementData +import no.nordicsemi.android.kotlin.ble.profile.bps.data.IntermediateCuffPressureData data class BPSServiceData ( val bloodPressureMeasurement: BloodPressureMeasurementData? = null, diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSSensorsReadingView.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSSensorsReadingView.kt index 2911be68..1e649b72 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSSensorsReadingView.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSSensorsReadingView.kt @@ -43,8 +43,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.bps.R import no.nordicsemi.android.bps.data.BPSServiceData -import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementData -import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureData +import no.nordicsemi.android.kotlin.ble.profile.bps.data.BloodPressureMeasurementData +import no.nordicsemi.android.kotlin.ble.profile.bps.data.IntermediateCuffPressureData import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index d51294ca..920234d6 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -59,9 +59,9 @@ import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser -import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementData +import no.nordicsemi.android.kotlin.ble.profile.bps.data.BloodPressureMeasurementData import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementParser -import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureData +import no.nordicsemi.android.kotlin.ble.profile.bps.data.IntermediateCuffPressureData import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureParser import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import java.util.* diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt index 70149f3f..d0a4af36 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt @@ -1,7 +1,7 @@ package no.nordicsemi.android.csc.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData +import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData data class CSCServiceData( val data: CSCData = CSCData(), diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index ad90c346..a149395c 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -42,9 +42,9 @@ import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData -import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize -import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSizes +import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData +import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSize +import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSizes import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt index c2204ab8..a970e2f8 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt @@ -50,8 +50,8 @@ import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.view.RadioButtonGroup import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.data.CSCServiceData -import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData -import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize +import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData +import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSize import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle import no.nordicsemi.android.ui.view.dialog.FlowCanceled diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt index 0a186798..660972c4 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt @@ -33,7 +33,7 @@ package no.nordicsemi.android.csc.view import no.nordicsemi.android.common.theme.view.RadioButtonItem import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity -import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData +import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData import java.util.* private const val DISPLAY_M_S = "m/s" diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt index b0ba0c84..87f510db 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt @@ -31,7 +31,7 @@ package no.nordicsemi.android.csc.view -import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize +import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSize internal sealed class CSCViewEvent diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt index 98393d66..a6b1d1ec 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt @@ -47,7 +47,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.csc.R -import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData +import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData @Composable internal fun WheelSizeView(state: CSCData, onClick: () -> Unit) { diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt index ba51274a..c260f292 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt @@ -153,8 +153,7 @@ internal class GLSManager( private suspend fun onNumberOfRecordsReceived(response: RecordAccessControlPointResponse) { if (response.numberOfRecords > 0) { if (data.value.records.isNotEmpty()) { - val sequenceNumber = data.value.records - .last().sequenceNumber + 1 + val sequenceNumber = data.value.records.last().sequenceNumber + 1 writeCharacteristic( recordAccessControlPointCharacteristic, RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo( diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt index 8d326ecd..40f9bdb1 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt @@ -32,7 +32,7 @@ package no.nordicsemi.android.hrs.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSData +import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData internal data class HRSServiceData( val data: List = emptyList(), diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 45e49803..9f3e8ff5 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -42,7 +42,7 @@ import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSData +import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt index ab54a19f..e5273518 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt @@ -32,7 +32,7 @@ package no.nordicsemi.android.hts.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.hts.HTSData +import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData internal data class HTSServiceData( val data: HTSData = HTSData(), diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index e3ee177d..01de2357 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -42,7 +42,7 @@ import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.hts.HTSData +import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt index 8c2578a0..1e01e679 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt @@ -34,7 +34,7 @@ package no.nordicsemi.android.rscs.data import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSData +import no.nordicsemi.android.kotlin.ble.profile.rscs.data.RSCSData import no.nordicsemi.android.rscs.R internal data class RSCSServiceData( diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 5490d379..a96cef9c 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -41,7 +41,7 @@ import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSData +import no.nordicsemi.android.kotlin.ble.profile.rscs.data.RSCSData import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager From 2ce7303bdaf38cc62e34c551e76e542c110e00fb Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 15 Mar 2023 12:07:37 +0100 Subject: [PATCH 012/101] Migrate GLS profile --- .../nordicsemi/android/gls/data/GLSManager.kt | 7 +- .../data/{GLSData.kt => GLSServiceData.kt} | 5 +- .../android/gls/main/view/GLSContentView.kt | 10 +- .../android/gls/main/view/GLSState.kt | 19 ++- .../gls/main/viewmodel/GLSViewModel.kt | 113 +++++++++++++++++- .../android/gls/repository/GLSRepository.kt | 72 +---------- 6 files changed, 134 insertions(+), 92 deletions(-) rename profile_gls/src/main/java/no/nordicsemi/android/gls/data/{GLSData.kt => GLSServiceData.kt} (91%) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt index c260f292..3ecb8d4d 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt @@ -60,8 +60,7 @@ private val GF_CHARACTERISTIC = UUID.fromString("00002A51-0000-1000-8000-00805f9 private val RACP_CHARACTERISTIC = UUID.fromString("00002A52-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") +private val BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb") internal class GLSManager( context: Context, @@ -74,8 +73,8 @@ internal class GLSManager( private var glucoseMeasurementContextCharacteristic: BluetoothGattCharacteristic? = null private var recordAccessControlPointCharacteristic: BluetoothGattCharacteristic? = null - private val data = MutableStateFlow(GLSData()) - val dataHolder = ConnectionObserverAdapter() + private val data = MutableStateFlow(GLSServiceData()) + val dataHolder = ConnectionObserverAdapter() init { connectionObserver = dataHolder diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSData.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt similarity index 91% rename from profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSData.kt rename to profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt index 7f973f1d..c8637c27 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSData.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt @@ -31,8 +31,11 @@ package no.nordicsemi.android.gls.data -internal data class GLSData( +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState + +internal data class GLSServiceData( val records: List = emptyList(), val batteryLevel: Int? = null, + val connectionState: GattConnectionState? = null, val requestStatus: RequestStatus = RequestStatus.IDLE ) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt index 2657c505..958e8100 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt @@ -57,7 +57,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.gls.R -import no.nordicsemi.android.gls.data.GLSData +import no.nordicsemi.android.gls.data.GLSServiceData import no.nordicsemi.android.gls.data.GLSRecord import no.nordicsemi.android.gls.data.RequestStatus import no.nordicsemi.android.gls.data.WorkingMode @@ -67,7 +67,7 @@ import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun GLSContentView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Unit) { +internal fun GLSContentView(state: GLSServiceData, onEvent: (GLSScreenViewEvent) -> Unit) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally @@ -97,7 +97,7 @@ internal fun GLSContentView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Uni } @Composable -private fun SettingsView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Unit) { +private fun SettingsView(state: GLSServiceData, onEvent: (GLSScreenViewEvent) -> Unit) { ScreenSection { SectionTitle(icon = Icons.Default.Settings, title = "Request items") @@ -121,7 +121,7 @@ private fun SettingsView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Unit) } @Composable -private fun RecordsView(state: GLSData) { +private fun RecordsView(state: GLSServiceData) { ScreenSection { if (state.records.isEmpty()) { RecordsViewWithoutData() @@ -133,7 +133,7 @@ private fun RecordsView(state: GLSData) { } @Composable -private fun RecordsViewWithData(state: GLSData) { +private fun RecordsViewWithData(state: GLSServiceData) { Column(modifier = Modifier.fillMaxWidth()) { SectionTitle(resId = R.drawable.ic_records, title = "Records") diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt index c91183e1..3acd3eeb 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt @@ -31,10 +31,19 @@ package no.nordicsemi.android.gls.main.view -import no.nordicsemi.android.gls.data.GLSData -import no.nordicsemi.android.service.BleManagerResult +import no.nordicsemi.android.gls.data.GLSServiceData +import no.nordicsemi.android.gls.data.RequestStatus -internal sealed class GLSViewState +internal data class GLSViewState( + val glsServiceData: GLSServiceData = GLSServiceData(), + val deviceName: String? = null +) { -internal data class WorkingState(val result: BleManagerResult) : GLSViewState() -internal object NoDeviceState : GLSViewState() + fun copyAndClear(): GLSViewState { + return copy(glsServiceData = glsServiceData.copy(records = emptyList(), requestStatus = RequestStatus.IDLE)) + } + + fun copyWithNewRequestStatus(requestStatus: RequestStatus): GLSViewState { + return copy(glsServiceData = glsServiceData.copy(requestStatus = requestStatus)) + } +} diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 7513f8c9..fa188806 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -31,43 +31,77 @@ package no.nordicsemi.android.gls.main.viewmodel +import android.annotation.SuppressLint +import android.bluetooth.BluetoothGattCharacteristic +import android.content.Context import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent +import no.nordicsemi.android.ble.ktx.suspend import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.GlsDetailsDestinationId import no.nordicsemi.android.gls.data.GLS_SERVICE_UUID +import no.nordicsemi.android.gls.data.RequestStatus import no.nordicsemi.android.gls.main.view.DisconnectEvent import no.nordicsemi.android.gls.main.view.GLSScreenViewEvent import no.nordicsemi.android.gls.main.view.GLSViewState -import no.nordicsemi.android.gls.main.view.NoDeviceState import no.nordicsemi.android.gls.main.view.OnGLSRecordClick import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected import no.nordicsemi.android.gls.main.view.OpenLoggerEvent -import no.nordicsemi.android.gls.main.view.WorkingState -import no.nordicsemi.android.gls.repository.GLSRepository import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RecordAccessControlPointData +import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSDataParser import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId +import no.nordicsemi.android.utils.launchWithCatch +import java.util.* import javax.inject.Inject +val GLS_SERVICE_UUID: UUID = UUID.fromString("00001808-0000-1000-8000-00805f9b34fb") + +private val GM_CHARACTERISTIC = UUID.fromString("00002A18-0000-1000-8000-00805f9b34fb") +private val GM_CONTEXT_CHARACTERISTIC = UUID.fromString("00002A34-0000-1000-8000-00805f9b34fb") +private val GF_CHARACTERISTIC = UUID.fromString("00002A51-0000-1000-8000-00805f9b34fb") +private val RACP_CHARACTERISTIC = UUID.fromString("00002A52-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") + +@SuppressLint("MissingPermission") @HiltViewModel internal class GLSViewModel @Inject constructor( - private val repository: GLSRepository, + @ApplicationContext + private val context: Context, private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(NoDeviceState) + private lateinit var client: BleGattClient + + private lateinit var glucoseMeasurementCharacteristic: BleGattCharacteristic + private lateinit var glucoseMeasurementContextCharacteristic: BleGattCharacteristic + private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic + + private val _state = MutableStateFlow(GLSViewState()) val state = _state.asStateFlow() init { @@ -81,7 +115,7 @@ internal class GLSViewModel @Inject constructor( private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() - is NavigationResult.Success -> connectDevice(result.value) + is NavigationResult.Success -> onDeviceSelected(result.value) } } @@ -95,6 +129,11 @@ internal class GLSViewModel @Inject constructor( } } + private fun onDeviceSelected(device: ServerDevice) { + _state.value = _state.value.copy(deviceName = device.name) + startGattClient(device) + } + private fun connectDevice(device: ServerDevice) { repository.downloadData(viewModelScope, device).onEach { _state.value = WorkingState(it) @@ -104,4 +143,66 @@ internal class GLSViewModel @Inject constructor( } }.launchIn(viewModelScope) } + + private fun startGattClient(blinkyDevice: ServerDevice) = viewModelScope.launch { + client = blinkyDevice.connect(context) + + client.connectionState + .onEach { _state.value = _state.value.copy() } + .filterNotNull() + .onEach { stopIfDisconnected(it) } + .launchIn(viewModelScope) + + client.services + .filterNotNull() + .onEach { configureGatt(it) } + .launchIn(viewModelScope) + } + + private suspend fun configureGatt(services: BleGattServices) { + val glsService = services.findService(GLS_SERVICE_UUID)!! + glucoseMeasurementCharacteristic = glsService.findCharacteristic(GM_CHARACTERISTIC)!! + glucoseMeasurementContextCharacteristic = glsService.findCharacteristic(GM_CONTEXT_CHARACTERISTIC)!! + recordAccessControlPointCharacteristic = glsService.findCharacteristic(RACP_CHARACTERISTIC)!! + val batteryService = services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + batteryLevelCharacteristic.getNotifications() + .mapNotNull { BatteryLevelParser.parse(it) } + .onEach { repository.onBatteryLevelChanged(it) } + .launchIn(viewModelScope) + + htsMeasurementCharacteristic.getNotifications() + .mapNotNull { HRSDataParser.parse(it) } + .onEach { repository.onHRSDataChanged(it) } + .launchIn(viewModelScope) + } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + stopSelf() + } + } + + private fun clear() { + _state.value = _state.value.copyAndClear() + } + + suspend fun requestLastRecord() { + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) + clear() + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) + } + + suspend fun requestFirstRecord() { + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) + clear() + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) + } + + suspend fun requestAllRecords() { + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) + clear() + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) + } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt index 86050cca..c0c0ff6e 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt @@ -31,82 +31,12 @@ package no.nordicsemi.android.gls.repository -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ViewModelScoped -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow -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.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory -import no.nordicsemi.android.gls.data.GLSData -import no.nordicsemi.android.gls.data.GLSManager -import no.nordicsemi.android.gls.data.WorkingMode -import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject @ViewModelScoped internal class GLSRepository @Inject constructor( - @ApplicationContext - private val context: Context, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst + ) { - private var manager: GLSManager? = null - private var logger: NordicLogger? = null - - fun downloadData(scope: CoroutineScope, device: ServerDevice): Flow> = callbackFlow { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "GLS", device.address).also { - logger = it - } - val managerInstance = manager ?: GLSManager(context, scope, createdLogger) - manager = managerInstance - - managerInstance.dataHolder.status.onEach { - send(it) - }.launchIn(scope) - - scope.launch { - managerInstance.start(device) - } - - awaitClose { - launch { - manager?.disconnect()?.suspend() - logger = null - manager = null - } - } - } - - private suspend fun GLSManager.start(device: ServerDevice) { -// try { -// connect(device.device) -// .useAutoConnect(false) -// .retry(3, 100) -// .suspend() -// } catch (e: Exception) { -// e.printStackTrace() -// } - } - - fun openLogger() { - NordicLogger.launch(context, logger) - } - - fun requestMode(workingMode: WorkingMode) { - when (workingMode) { - WorkingMode.ALL -> manager?.requestAllRecords() - WorkingMode.LAST -> manager?.requestLastRecord() - WorkingMode.FIRST -> manager?.requestFirstRecord() - } - } } From cc9d4d225a7f78ca4292c809150cd587254403d6 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 15 Mar 2023 16:47:12 +0100 Subject: [PATCH 013/101] Migrate GLS WIP --- .../nordicsemi/android/gls/data/GLSManager.kt | 2 +- .../android/gls/data/GLSServiceData.kt | 3 +- .../android/gls/main/view/GLSState.kt | 2 +- .../gls/main/viewmodel/GLSViewModel.kt | 88 +++++++++++++++++-- 4 files changed, 85 insertions(+), 10 deletions(-) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt index 3ecb8d4d..6166ffc1 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt @@ -200,7 +200,7 @@ internal class GLSManager( } private fun clear() { - data.tryEmit(data.value.copy(records = emptyList())) + data.tryEmit(data.value.copy(records = mapOf())) val target = bluetoothDevice if (target != null) { data.tryEmit(data.value.copy(requestStatus = RequestStatus.SUCCESS)) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt index c8637c27..0f621f9a 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt @@ -32,9 +32,10 @@ package no.nordicsemi.android.gls.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GlucoseMeasurementContext internal data class GLSServiceData( - val records: List = emptyList(), + val records: Map = mapOf(), val batteryLevel: Int? = null, val connectionState: GattConnectionState? = null, val requestStatus: RequestStatus = RequestStatus.IDLE diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt index 3acd3eeb..646eb79b 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt @@ -40,7 +40,7 @@ internal data class GLSViewState( ) { fun copyAndClear(): GLSViewState { - return copy(glsServiceData = glsServiceData.copy(records = emptyList(), requestStatus = RequestStatus.IDLE)) + return copy(glsServiceData = glsServiceData.copy(records = mapOf(), requestStatus = RequestStatus.IDLE)) } fun copyWithNewRequestStatus(requestStatus: RequestStatus): GLSViewState { diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index fa188806..0a83ecca 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -32,7 +32,6 @@ package no.nordicsemi.android.gls.main.viewmodel import android.annotation.SuppressLint -import android.bluetooth.BluetoothGattCharacteristic import android.content.Context import android.os.ParcelUuid import androidx.lifecycle.ViewModel @@ -49,12 +48,14 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent -import no.nordicsemi.android.ble.ktx.suspend +import no.nordicsemi.android.ble.common.callback.RecordAccessControlPointDataCallback +import no.nordicsemi.android.ble.common.callback.RecordAccessControlPointResponse import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.GlsDetailsDestinationId import no.nordicsemi.android.gls.data.GLS_SERVICE_UUID import no.nordicsemi.android.gls.data.RequestStatus +import no.nordicsemi.android.gls.data.WorkingMode import no.nordicsemi.android.gls.main.view.DisconnectEvent import no.nordicsemi.android.gls.main.view.GLSScreenViewEvent import no.nordicsemi.android.gls.main.view.GLSViewState @@ -67,12 +68,15 @@ import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristi import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementContextParser +import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser +import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointParser +import no.nordicsemi.android.kotlin.ble.profile.gls.data.NumberOfRecordsData import no.nordicsemi.android.kotlin.ble.profile.gls.data.RecordAccessControlPointData -import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSDataParser +import no.nordicsemi.android.kotlin.ble.profile.gls.data.ResponseData import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId -import no.nordicsemi.android.utils.launchWithCatch import java.util.* import javax.inject.Inject @@ -134,6 +138,14 @@ internal class GLSViewModel @Inject constructor( startGattClient(device) } + private fun onEvent(event: OnWorkingModeSelected) = viewModelScope.launch { + when (event.workingMode) { + WorkingMode.ALL -> requestAllRecords() + WorkingMode.LAST -> requestLastRecord() + WorkingMode.FIRST -> requestFirstRecord() + } + } + private fun connectDevice(device: ServerDevice) { repository.downloadData(viewModelScope, device).onEach { _state.value = WorkingState(it) @@ -172,9 +184,19 @@ internal class GLSViewModel @Inject constructor( .onEach { repository.onBatteryLevelChanged(it) } .launchIn(viewModelScope) - htsMeasurementCharacteristic.getNotifications() - .mapNotNull { HRSDataParser.parse(it) } - .onEach { repository.onHRSDataChanged(it) } + glucoseMeasurementCharacteristic.getNotifications() + .mapNotNull { GlucoseMeasurementParser.parse(it) } + .onEach { } + .launchIn(viewModelScope) + + glucoseMeasurementContextCharacteristic.getNotifications() + .mapNotNull { GlucoseMeasurementContextParser.parse(it) } + .onEach { } + .launchIn(viewModelScope) + + recordAccessControlPointCharacteristic.getNotifications() + .mapNotNull { RecordAccessControlPointParser.parse(it) } + .onEach { onAccessControlPointDataReceived(it) } .launchIn(viewModelScope) } @@ -184,6 +206,58 @@ internal class GLSViewModel @Inject constructor( } } + private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) { + when (data) { + is NumberOfRecordsData -> if () + is ResponseData -> TODO() + } + if (it.isOperationCompleted && it.wereRecordsFound() && it.numberOfRecords > 0) { + onNumberOfRecordsReceived(it) + } else if (it.isOperationCompleted && it.wereRecordsFound() && it.numberOfRecords == 0) { + onRecordAccessOperationCompletedWithNoRecordsFound(it) + } else if (it.isOperationCompleted && it.wereRecordsFound()) { + onRecordAccessOperationCompleted(it) + } else if (it.errorCode > 0) { + onRecordAccessOperationError(it) + } + } + + private fun onRecordAccessOperationCompleted(response: RecordAccessControlPointResponse) { + val status = when (response.requestCode) { + RecordAccessControlPointDataCallback.RACP_OP_CODE_ABORT_OPERATION -> RequestStatus.ABORTED + else -> RequestStatus.SUCCESS + } + _state.value = _state.value.copyWithNewRequestStatus(status) + } + + private fun onRecordAccessOperationCompletedWithNoRecordsFound(response: RecordAccessControlPointResponse) { + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.SUCCESS) + } + + private suspend fun onNumberOfRecordsReceived(response: RecordAccessControlPointResponse) { + if (response.numberOfRecords > 0) { + if (data.value.records.isNotEmpty()) { + val sequenceNumber = data.value.records.last().sequenceNumber + 1 + recordAccessControlPointCharacteristic.write( + RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber).value + ) + } else { + recordAccessControlPointCharacteristic.write( + RecordAccessControlPointInputParser.reportAllStoredRecords().value + ) + } + } + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.SUCCESS) + } + + private fun onRecordAccessOperationError(response: RecordAccessControlPointResponse) { + if (response.errorCode == RecordAccessControlPointDataCallback.RACP_ERROR_OP_CODE_NOT_SUPPORTED) { + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.NOT_SUPPORTED) + } else { + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) + } + } + private fun clear() { _state.value = _state.value.copyAndClear() } From e15ad2967ea2ef81627f515b9130839af23fbe97 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 16 Mar 2023 09:40:23 +0100 Subject: [PATCH 014/101] Finish migrating GLS profile --- gradle.properties | 2 - .../android/bps/viewmodel/BPSViewModel.kt | 6 +- .../android/csc/repository/CSCService.kt | 4 +- .../nordicsemi/android/gls/GLSDestination.kt | 5 +- .../nordicsemi/android/gls/data/DataMapper.kt | 74 ------ .../nordicsemi/android/gls/data/GLSManager.kt | 249 ------------------ .../nordicsemi/android/gls/data/GLSRecord.kt | 127 --------- .../android/gls/data/GLSServiceData.kt | 6 +- .../android/gls/data/RequestStatus.kt | 36 --- .../gls/details/view/GLSDetailsContentView.kt | 102 +++---- .../gls/details/view/GLSDetailsMappers.kt | 20 +- .../gls/details/view/GLSDetailsScreen.kt | 2 +- .../android/gls/main/view/GLSContentView.kt | 19 +- .../android/gls/main/view/GLSMapper.kt | 4 +- .../android/gls/main/view/GLSScreen.kt | 40 +-- .../gls/main/view/GLSScreenViewEvent.kt | 2 +- .../android/gls/main/view/GLSState.kt | 29 +- .../gls/main/viewmodel/GLSViewModel.kt | 131 ++++----- .../android/hrs/service/HRSService.kt | 4 +- .../android/hts/repository/HTSService.kt | 4 +- .../android/rscs/repository/RSCSService.kt | 4 +- 21 files changed, 206 insertions(+), 664 deletions(-) delete mode 100644 profile_gls/src/main/java/no/nordicsemi/android/gls/data/DataMapper.kt delete mode 100644 profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt delete mode 100644 profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSRecord.kt delete mode 100644 profile_gls/src/main/java/no/nordicsemi/android/gls/data/RequestStatus.kt diff --git a/gradle.properties b/gradle.properties index 7b00938f..7c49c88c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -46,7 +46,5 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official \ No newline at end of file diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 920234d6..615d69d1 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -110,10 +110,10 @@ internal class BPSViewModel @Inject constructor( } } - private fun startGattClient(blinkyDevice: ServerDevice) = viewModelScope.launch { - _state.value = _state.value.copy(deviceName = blinkyDevice.name) + private fun startGattClient(device: ServerDevice) = viewModelScope.launch { + _state.value = _state.value.copy(deviceName = device.name) - client = blinkyDevice.connect(context) + client = device.connect(context) client.connectionState .filterNotNull() diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 89c4a506..f9d0f303 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -80,8 +80,8 @@ internal class CSCService : NotificationService() { return START_REDELIVER_INTENT } - private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { - client = blinkyDevice.connect(this@CSCService) + private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { + client = device.connect(this@CSCService) client.connectionState .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSDestination.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSDestination.kt index 00e0ef92..a39603e1 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSDestination.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSDestination.kt @@ -33,9 +33,10 @@ package no.nordicsemi.android.gls import no.nordicsemi.android.common.navigation.createDestination import no.nordicsemi.android.common.navigation.defineDestination -import no.nordicsemi.android.gls.data.GLSRecord import no.nordicsemi.android.gls.details.view.GLSDetailsScreen +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext -internal val GlsDetailsDestinationId = createDestination("gls-details-screen") +internal val GlsDetailsDestinationId = createDestination, Unit>("gls-details-screen") val GLSDestination = defineDestination(GlsDetailsDestinationId) { GLSDetailsScreen() } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/DataMapper.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/DataMapper.kt deleted file mode 100644 index 46657dea..00000000 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/DataMapper.kt +++ /dev/null @@ -1,74 +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.gls.data - -import no.nordicsemi.android.ble.common.callback.glucose.GlucoseMeasurementContextResponse -import no.nordicsemi.android.ble.common.callback.glucose.GlucoseMeasurementResponse - -internal fun GlucoseMeasurementResponse.toRecord(): GLSRecord { - return this.let { - GLSRecord( - sequenceNumber = it.sequenceNumber, - time = it.time, - glucoseConcentration = it.glucoseConcentration ?: 0f, - unit = it.unit?.let { ConcentrationUnit.create(it) } - ?: ConcentrationUnit.UNIT_KGPL, - type = RecordType.createOrNull(it.type), - sampleLocation = SampleLocation.createOrNull(it.sampleLocation), - status = it.status - ) - } -} - -internal fun GlucoseMeasurementContextResponse.toMeasurementContext(): MeasurementContext { - return this.let { - MeasurementContext( - sequenceNumber = it.sequenceNumber, - carbohydrate = it.carbohydrate, - carbohydrateAmount = it.carbohydrateAmount ?: 0f, - meal = it.meal, - tester = it.tester, - health = it.health, - exerciseDuration = it.exerciseDuration ?: 0, - exerciseIntensity = it.exerciseIntensity ?: 0, - medication = it.medication, - medicationQuantity = it.medicationAmount ?: 0f, - medicationUnit = it.medicationUnit?.let { MedicationUnit.create(it) } - ?: MedicationUnit.UNIT_KG, - HbA1c = it.hbA1c ?: 0f - ) - } -} - -internal fun GLSRecord.copyWithNewContext(response: GlucoseMeasurementContextResponse): GLSRecord { - return copy(context = context) -} diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt deleted file mode 100644 index 6166ffc1..00000000 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSManager.kt +++ /dev/null @@ -1,249 +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.gls.data - -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic -import android.content.Context -import android.util.Log -import kotlinx.coroutines.CoroutineScope -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.RecordAccessControlPointDataCallback -import no.nordicsemi.android.ble.common.callback.RecordAccessControlPointResponse -import no.nordicsemi.android.ble.common.callback.battery.BatteryLevelResponse -import no.nordicsemi.android.ble.common.callback.glucose.GlucoseMeasurementContextResponse -import no.nordicsemi.android.ble.common.callback.glucose.GlucoseMeasurementResponse -import no.nordicsemi.android.ble.common.data.RecordAccessControlPointData -import no.nordicsemi.android.ble.ktx.asValidResponseFlow -import no.nordicsemi.android.ble.ktx.suspend -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.service.ConnectionObserverAdapter -import no.nordicsemi.android.utils.launchWithCatch -import java.util.* - -val GLS_SERVICE_UUID: UUID = UUID.fromString("00001808-0000-1000-8000-00805f9b34fb") - -private val GM_CHARACTERISTIC = UUID.fromString("00002A18-0000-1000-8000-00805f9b34fb") -private val GM_CONTEXT_CHARACTERISTIC = UUID.fromString("00002A34-0000-1000-8000-00805f9b34fb") -private val GF_CHARACTERISTIC = UUID.fromString("00002A51-0000-1000-8000-00805f9b34fb") -private val RACP_CHARACTERISTIC = UUID.fromString("00002A52-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 GLSManager( - context: Context, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : BleManager(context) { - - private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null - private var glucoseMeasurementCharacteristic: BluetoothGattCharacteristic? = null - private var glucoseMeasurementContextCharacteristic: BluetoothGattCharacteristic? = null - private var recordAccessControlPointCharacteristic: BluetoothGattCharacteristic? = null - - private val data = MutableStateFlow(GLSServiceData()) - val dataHolder = ConnectionObserverAdapter() - - init { - connectionObserver = dataHolder - - data.onEach { - dataHolder.setValue(it) - }.launchIn(scope) - } - - override fun log(priority: Int, message: String) { - logger.log(priority, message) - } - - override fun getMinLogPriority(): Int { - return Log.VERBOSE - } - - override fun getGattCallback(): BleManagerGattCallback { - return GlucoseManagerGattCallback() - } - - private inner class GlucoseManagerGattCallback : BleManagerGattCallback() { - override fun initialize() { - super.initialize() - - setNotificationCallback(glucoseMeasurementCharacteristic).asValidResponseFlow() - .onEach { data.tryEmit(data.value.copy(records = data.value.records + it.toRecord())) } - .launchIn(scope) - - setNotificationCallback(glucoseMeasurementContextCharacteristic).asValidResponseFlow() - .onEach { - val context = it.toMeasurementContext() - data.value.records.find { context.sequenceNumber == it.sequenceNumber }?.let { - it.context = context - } - data.tryEmit(data.value) - }.launchIn(scope) - - setIndicationCallback(recordAccessControlPointCharacteristic).asValidResponseFlow() - .onEach { - if (it.isOperationCompleted && it.wereRecordsFound() && it.numberOfRecords > 0) { - onNumberOfRecordsReceived(it) - } else if (it.isOperationCompleted && it.wereRecordsFound() && it.numberOfRecords == 0) { - onRecordAccessOperationCompletedWithNoRecordsFound(it) - } else if (it.isOperationCompleted && it.wereRecordsFound()) { - onRecordAccessOperationCompleted(it) - } else if (it.errorCode > 0) { - onRecordAccessOperationError(it) - } - }.launchIn(scope) - - setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow() - .onEach { - data.value = data.value.copy(batteryLevel = it.batteryLevel) - }.launchIn(scope) - - enableNotifications(glucoseMeasurementCharacteristic).enqueue() - enableNotifications(glucoseMeasurementContextCharacteristic).enqueue() - enableIndications(recordAccessControlPointCharacteristic).enqueue() - enableNotifications(batteryLevelCharacteristic).enqueue() - } - - private fun onRecordAccessOperationCompleted(response: RecordAccessControlPointResponse) { - val status = when (response.requestCode) { - RecordAccessControlPointDataCallback.RACP_OP_CODE_ABORT_OPERATION -> RequestStatus.ABORTED - else -> RequestStatus.SUCCESS - } - data.tryEmit(data.value.copy(requestStatus = status)) - } - - private fun onRecordAccessOperationCompletedWithNoRecordsFound(response: RecordAccessControlPointResponse) { - data.tryEmit(data.value.copy(requestStatus = RequestStatus.SUCCESS)) - } - - private suspend fun onNumberOfRecordsReceived(response: RecordAccessControlPointResponse) { - if (response.numberOfRecords > 0) { - if (data.value.records.isNotEmpty()) { - val sequenceNumber = data.value.records.last().sequenceNumber + 1 - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo( - sequenceNumber - ), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } else { - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportAllStoredRecords(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } - } - data.tryEmit(data.value.copy(requestStatus = RequestStatus.SUCCESS)) - } - - private fun onRecordAccessOperationError(response: RecordAccessControlPointResponse) { - log(Log.WARN, "Record Access operation failed (error ${response.errorCode})") - if (response.errorCode == RecordAccessControlPointDataCallback.RACP_ERROR_OP_CODE_NOT_SUPPORTED) { - data.tryEmit(data.value.copy(requestStatus = RequestStatus.NOT_SUPPORTED)) - } else { - data.tryEmit(data.value.copy(requestStatus = RequestStatus.FAILED)) - } - } - - public override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - gatt.getService(GLS_SERVICE_UUID)?.run { - glucoseMeasurementCharacteristic = getCharacteristic(GM_CHARACTERISTIC) - glucoseMeasurementContextCharacteristic = getCharacteristic(GM_CONTEXT_CHARACTERISTIC) - recordAccessControlPointCharacteristic = getCharacteristic(RACP_CHARACTERISTIC) - } - gatt.getService(BATTERY_SERVICE_UUID)?.run { - batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID) - } - return glucoseMeasurementCharacteristic != null && recordAccessControlPointCharacteristic != null - } - - override fun onServicesInvalidated() { - glucoseMeasurementCharacteristic = null - glucoseMeasurementContextCharacteristic = null - recordAccessControlPointCharacteristic = null - } - } - - private fun clear() { - data.tryEmit(data.value.copy(records = mapOf())) - val target = bluetoothDevice - if (target != null) { - data.tryEmit(data.value.copy(requestStatus = RequestStatus.SUCCESS)) - } - } - - fun requestLastRecord() { - if (recordAccessControlPointCharacteristic == null) return - val target = bluetoothDevice ?: return - clear() - data.tryEmit(data.value.copy(requestStatus = RequestStatus.PENDING)) - scope.launchWithCatch { - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportLastStoredRecord(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } - } - - fun requestFirstRecord() { - if (recordAccessControlPointCharacteristic == null) return - clear() - data.tryEmit(data.value.copy(requestStatus = RequestStatus.PENDING)) - scope.launchWithCatch { - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportFirstStoredRecord(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } - } - - fun requestAllRecords() { - if (recordAccessControlPointCharacteristic == null) return - clear() - data.tryEmit(data.value.copy(requestStatus = RequestStatus.PENDING)) - scope.launchWithCatch { - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportNumberOfAllStoredRecords(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } - } -} diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSRecord.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSRecord.kt deleted file mode 100644 index cc4ec51c..00000000 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSRecord.kt +++ /dev/null @@ -1,127 +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.gls.data - -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementCallback.GlucoseStatus -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Carbohydrate -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Health -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Meal -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Medication -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Tester -import java.util.* - -internal data class GLSRecord( - val sequenceNumber: Int = 0, - val time: Calendar? = null, - val glucoseConcentration: Float = 0f, - val unit: ConcentrationUnit = ConcentrationUnit.UNIT_KGPL, - val type: RecordType? = null, - val status: GlucoseStatus? = null, - val sampleLocation: SampleLocation? = null, - var context: MeasurementContext? = null -) - -internal enum class RecordType(val id: Int) { - CAPILLARY_WHOLE_BLOOD(1), - CAPILLARY_PLASMA(2), - VENOUS_WHOLE_BLOOD(3), - VENOUS_PLASMA(4), - ARTERIAL_WHOLE_BLOOD(5), - ARTERIAL_PLASMA(6), - UNDETERMINED_WHOLE_BLOOD(7), - UNDETERMINED_PLASMA(8), - INTERSTITIAL_FLUID(9), - CONTROL_SOLUTION(10); - - companion object { - fun create(value: Int): RecordType { - return values().firstOrNull { it.id == value.toInt() } - ?: throw IllegalArgumentException("Cannot find element for provided value.") - } - - fun createOrNull(value: Int?): RecordType? { - return values().firstOrNull { it.id == value } - } - } -} - -internal data class MeasurementContext( - val sequenceNumber: Int = 0, - val carbohydrate: Carbohydrate? = null, - val carbohydrateAmount: Float = 0f, - val meal: Meal? = null, - val tester: Tester? = null, - val health: Health? = null, - val exerciseDuration: Int = 0, - val exerciseIntensity: Int = 0, - val medication: Medication?, - val medicationQuantity: Float = 0f, - val medicationUnit: MedicationUnit = MedicationUnit.UNIT_KG, - val HbA1c: Float = 0f -) - -internal enum class ConcentrationUnit(val id: Int) { - UNIT_KGPL(0), - UNIT_MOLPL(1); - - companion object { - fun create(value: Int): ConcentrationUnit { - return values().firstOrNull { it.id == value } - ?: throw IllegalArgumentException("Cannot find element for provided value.") - } - } -} - -internal enum class MedicationUnit(val id: Int) { - UNIT_KG(0), - UNIT_L(1); - - companion object { - fun create(value: Int): MedicationUnit { - return values().firstOrNull { it.id == value } - ?: throw IllegalArgumentException("Cannot find element for provided value.") - } - } -} - -internal enum class SampleLocation(val id: Int) { - FINGER(1), - AST(2), - EARLOBE(3), - CONTROL_SOLUTION(4), - NOT_AVAILABLE(15); - - companion object { - fun createOrNull(value: Int?): SampleLocation? { - return values().firstOrNull { it.id == value } - } - } -} diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt index 0f621f9a..f5a5cb0a 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt @@ -32,10 +32,12 @@ package no.nordicsemi.android.gls.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.gls.data.GlucoseMeasurementContext +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus internal data class GLSServiceData( - val records: Map = mapOf(), + val records: Map = mapOf(), val batteryLevel: Int? = null, val connectionState: GattConnectionState? = null, val requestStatus: RequestStatus = RequestStatus.IDLE diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/RequestStatus.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/RequestStatus.kt deleted file mode 100644 index f369da4a..00000000 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/RequestStatus.kt +++ /dev/null @@ -1,36 +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.gls.data - -internal enum class RequestStatus { - IDLE, PENDING, SUCCESS, ABORTED, FAILED, NOT_SUPPORTED -} diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt index fccc7909..e0ca33e2 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt @@ -49,12 +49,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import no.nordicsemi.android.gls.R -import no.nordicsemi.android.gls.data.GLSRecord import no.nordicsemi.android.gls.main.view.toDisplayString +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext import no.nordicsemi.android.ui.view.ScreenSection @Composable -internal fun GLSDetailsContentView(record: GLSRecord) { +internal fun GLSDetailsContentView(record: GLSRecord, context: GLSMeasurementContext?) { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { Column(modifier = Modifier.padding(16.dp)) { ScreenSection { @@ -86,24 +87,28 @@ internal fun GLSDetailsContentView(record: GLSRecord) { Spacer(modifier = Modifier.size(4.dp)) } - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.Bottom - ) { - Text( - text = stringResource(id = R.string.gls_details_glucose_condensation_title), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.outline - ) - Text( - text = stringResource( - id = R.string.gls_details_glucose_condensation_field, - record.glucoseConcentration, - record.unit.toDisplayString() - ), - style = MaterialTheme.typography.titleLarge - ) + record.glucoseConcentration?.let { glucoseConcentration -> + record.unit?.let { unit -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Bottom + ) { + Text( + text = stringResource(id = R.string.gls_details_glucose_condensation_title), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.outline + ) + Text( + text = stringResource( + id = R.string.gls_details_glucose_condensation_field, + glucoseConcentration, + unit.toDisplayString() + ), + style = MaterialTheme.typography.titleLarge + ) + } + } } record.status?.let { @@ -172,7 +177,7 @@ internal fun GLSDetailsContentView(record: GLSRecord) { Spacer(modifier = Modifier.size(4.dp)) } - record.context?.let { + context?.let { Divider( color = MaterialTheme.colorScheme.secondary, thickness = 1.dp, @@ -209,33 +214,42 @@ internal fun GLSDetailsContentView(record: GLSRecord) { ) Spacer(modifier = Modifier.size(4.dp)) } - Field( - stringResource(id = R.string.gls_context_exercise_title), - stringResource( - id = R.string.gls_context_exercise_field, - it.exerciseDuration, - it.exerciseIntensity + it.exerciseDuration?.let { exerciseDuration -> + it.exerciseIntensity?.let { exerciseIntensity -> + Field( + stringResource(id = R.string.gls_context_exercise_title), + stringResource( + id = R.string.gls_context_exercise_field, + exerciseDuration, + exerciseIntensity + ) + ) + } + } + + it.medicationUnit?.let { medicationUnit -> + Spacer(modifier = Modifier.size(4.dp)) + val medicationField = String.format( + stringResource(id = R.string.gls_context_medication_field), + it.medicationQuantity, + medicationUnit.toDisplayString(), + it.medication?.toDisplayString() ) - ) - Spacer(modifier = Modifier.size(4.dp)) + Field( + stringResource(id = R.string.gls_context_medication_title), + medicationField + ) + } - val medicationField = String.format( - stringResource(id = R.string.gls_context_medication_field), - it.medicationQuantity, - it.medicationUnit.toDisplayString(), - it.medication?.toDisplayString() - ) - Field( - stringResource(id = R.string.gls_context_medication_title), - medicationField - ) + it.HbA1c?.let { hbA1c -> + Spacer(modifier = Modifier.size(4.dp)) + Field( + stringResource(id = R.string.gls_context_hba1c_title), + stringResource(id = R.string.gls_context_hba1c_field, hbA1c) + ) + } Spacer(modifier = Modifier.size(4.dp)) - Field( - stringResource(id = R.string.gls_context_hba1c_title), - stringResource(id = R.string.gls_context_hba1c_field, it.HbA1c) - ) - Spacer(modifier = Modifier.size(4.dp)) } ?: Field( stringResource(id = R.string.gls_context_title), stringResource(id = R.string.gls_unavailable) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsMappers.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsMappers.kt index 9d3401ca..63a6ddbb 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsMappers.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsMappers.kt @@ -33,15 +33,15 @@ package no.nordicsemi.android.gls.details.view import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Carbohydrate -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Health -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Meal -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Medication -import no.nordicsemi.android.ble.common.profile.glucose.GlucoseMeasurementContextCallback.Tester import no.nordicsemi.android.gls.R -import no.nordicsemi.android.gls.data.ConcentrationUnit -import no.nordicsemi.android.gls.data.MedicationUnit -import no.nordicsemi.android.gls.data.SampleLocation +import no.nordicsemi.android.kotlin.ble.profile.gls.data.Carbohydrate +import no.nordicsemi.android.kotlin.ble.profile.gls.data.ConcentrationUnit +import no.nordicsemi.android.kotlin.ble.profile.gls.data.Health +import no.nordicsemi.android.kotlin.ble.profile.gls.data.Meal +import no.nordicsemi.android.kotlin.ble.profile.gls.data.Medication +import no.nordicsemi.android.kotlin.ble.profile.gls.data.MedicationUnit +import no.nordicsemi.android.kotlin.ble.profile.gls.data.SampleLocation +import no.nordicsemi.android.kotlin.ble.profile.gls.data.Tester @Composable internal fun SampleLocation.toDisplayString(): String { @@ -65,8 +65,8 @@ internal fun ConcentrationUnit.toDisplayString(): String { @Composable internal fun MedicationUnit.toDisplayString(): String { return when (this) { - MedicationUnit.UNIT_KG -> stringResource(id = R.string.gls_sample_location_kg) - MedicationUnit.UNIT_L -> stringResource(id = R.string.gls_sample_location_l) + MedicationUnit.UNIT_MG -> stringResource(id = R.string.gls_sample_location_kg) + MedicationUnit.UNIT_ML -> stringResource(id = R.string.gls_sample_location_l) } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsScreen.kt index 07791fac..0a668199 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsScreen.kt @@ -50,6 +50,6 @@ internal fun GLSDetailsScreen() { viewModel.navigateBack() } - GLSDetailsContentView(record) + GLSDetailsContentView(record.first, record.second) } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt index 958e8100..f8c9830b 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSContentView.kt @@ -58,10 +58,10 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.gls.R import no.nordicsemi.android.gls.data.GLSServiceData -import no.nordicsemi.android.gls.data.GLSRecord -import no.nordicsemi.android.gls.data.RequestStatus import no.nordicsemi.android.gls.data.WorkingMode import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @@ -139,7 +139,7 @@ private fun RecordsViewWithData(state: GLSServiceData) { Spacer(modifier = Modifier.height(16.dp)) - state.records.forEachIndexed { i, it -> + state.records.keys.forEachIndexed { i, it -> RecordItem(it) if (i < state.records.size - 1) { @@ -184,13 +184,12 @@ private fun RecordItem(record: GLSRecord) { style = MaterialTheme.typography.bodySmall ) - Text( - text = glucoseConcentrationDisplayValue( - record.glucoseConcentration, - record.unit - ), - style = MaterialTheme.typography.labelLarge, - ) + record.glucoseConcentration?.let { glucoseConcentration -> record.unit?.let { unit -> + Text( + text = glucoseConcentrationDisplayValue(glucoseConcentration, unit), + style = MaterialTheme.typography.labelLarge, + ) + } } } } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSMapper.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSMapper.kt index 356531e8..0e5e8658 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSMapper.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSMapper.kt @@ -34,9 +34,9 @@ package no.nordicsemi.android.gls.main.view import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import no.nordicsemi.android.gls.R -import no.nordicsemi.android.gls.data.ConcentrationUnit -import no.nordicsemi.android.gls.data.RecordType import no.nordicsemi.android.gls.data.WorkingMode +import no.nordicsemi.android.kotlin.ble.profile.gls.data.ConcentrationUnit +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RecordType @Composable internal fun RecordType?.toDisplayString(): String { diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index 0f6e956e..7637ad12 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -48,15 +48,7 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.gls.R import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult -import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -78,19 +70,15 @@ fun GLSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state) { - NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.result) { - is IdleResult, - is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) } - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { - NavigateUpButton(navigateUp) - } - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - is SuccessResult -> GLSContentView(state.result.data) { viewModel.onEvent(it) } + if (state.deviceName == null) { + DeviceConnectingView() + } else { + when (state.glsServiceData.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> GLSContentView(state.glsServiceData) { viewModel.onEvent(it) } } } } @@ -99,14 +87,10 @@ fun GLSScreen() { @Composable private fun AppBar(state: GLSViewState, navigateUp: () -> Unit, viewModel: GLSViewModel) { - val toolbarName = (state as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { + if (state.deviceName == null) { BackIconAppBar(stringResource(id = R.string.gls_title), navigateUp) } else { - LoggerIconAppBar(toolbarName, { + LoggerIconAppBar(state.deviceName, { viewModel.onEvent(DisconnectEvent) }, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreenViewEvent.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreenViewEvent.kt index 1a964db9..2ca44d61 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreenViewEvent.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreenViewEvent.kt @@ -31,8 +31,8 @@ package no.nordicsemi.android.gls.main.view -import no.nordicsemi.android.gls.data.GLSRecord import no.nordicsemi.android.gls.data.WorkingMode +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord internal sealed class GLSScreenViewEvent diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt index 646eb79b..b77e792c 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt @@ -32,13 +32,20 @@ package no.nordicsemi.android.gls.main.view import no.nordicsemi.android.gls.data.GLSServiceData -import no.nordicsemi.android.gls.data.RequestStatus +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus internal data class GLSViewState( val glsServiceData: GLSServiceData = GLSServiceData(), val deviceName: String? = null ) { + fun copyWithNewConnectionState(connectionState: GattConnectionState): GLSViewState { + return copy(glsServiceData = glsServiceData.copy(connectionState = connectionState)) + } + fun copyAndClear(): GLSViewState { return copy(glsServiceData = glsServiceData.copy(records = mapOf(), requestStatus = RequestStatus.IDLE)) } @@ -46,4 +53,24 @@ internal data class GLSViewState( fun copyWithNewRequestStatus(requestStatus: RequestStatus): GLSViewState { return copy(glsServiceData = glsServiceData.copy(requestStatus = requestStatus)) } + + fun copyWithNewBatteryLevel(batteryLevel: Int): GLSViewState { + return copy(glsServiceData = glsServiceData.copy(batteryLevel = batteryLevel)) + } + + //todo optimise + fun copyWithNewRecord(record: GLSRecord): GLSViewState { + val records = glsServiceData.records.toMutableMap() + records[record] = null + return copy(glsServiceData = glsServiceData.copy(records = records.toMap())) + } + + //todo optimise + fun copyWithNewContext(context: GLSMeasurementContext): GLSViewState { + val records = glsServiceData.records.toMutableMap() + return records.keys.firstOrNull { it.sequenceNumber == context.sequenceNumber }?.let { + records[it] = context + copy(glsServiceData = glsServiceData.copy(records = records.toMap())) + } ?: this + } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 0a83ecca..c48be0b8 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -48,13 +48,9 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent -import no.nordicsemi.android.ble.common.callback.RecordAccessControlPointDataCallback -import no.nordicsemi.android.ble.common.callback.RecordAccessControlPointResponse import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.GlsDetailsDestinationId -import no.nordicsemi.android.gls.data.GLS_SERVICE_UUID -import no.nordicsemi.android.gls.data.RequestStatus import no.nordicsemi.android.gls.data.WorkingMode import no.nordicsemi.android.gls.main.view.DisconnectEvent import no.nordicsemi.android.gls.main.view.GLSScreenViewEvent @@ -72,10 +68,13 @@ import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementContextPar import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointParser +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord import no.nordicsemi.android.kotlin.ble.profile.gls.data.NumberOfRecordsData import no.nordicsemi.android.kotlin.ble.profile.gls.data.RecordAccessControlPointData +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.ResponseData -import no.nordicsemi.android.service.ConnectedResult +import no.nordicsemi.android.kotlin.ble.profile.racp.RACPOpCode +import no.nordicsemi.android.kotlin.ble.profile.racp.RACPResponseCode import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import java.util.* import javax.inject.Inject @@ -102,12 +101,14 @@ internal class GLSViewModel @Inject constructor( private lateinit var client: BleGattClient private lateinit var glucoseMeasurementCharacteristic: BleGattCharacteristic - private lateinit var glucoseMeasurementContextCharacteristic: BleGattCharacteristic private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic private val _state = MutableStateFlow(GLSViewState()) val state = _state.asStateFlow() + private val highestSequenceNumber + get() = state.value.glsServiceData.records.keys.maxByOrNull { it.sequenceNumber }?.sequenceNumber ?: -1 + init { navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(GLS_SERVICE_UUID)) @@ -125,16 +126,20 @@ internal class GLSViewModel @Inject constructor( fun onEvent(event: GLSScreenViewEvent) { when (event) { - OpenLoggerEvent -> repository.openLogger() + OpenLoggerEvent -> TODO() DisconnectEvent -> navigationManager.navigateUp() - is OnWorkingModeSelected -> repository.requestMode(event.workingMode) - is OnGLSRecordClick -> navigationManager.navigateTo(GlsDetailsDestinationId, event.record) + is OnWorkingModeSelected -> onEvent(event) + is OnGLSRecordClick -> navigateToDetails(event.record) DisconnectEvent -> navigationManager.navigateUp() } } + private fun navigateToDetails(record: GLSRecord) { + val context = state.value.glsServiceData.records[record] + navigationManager.navigateTo(GlsDetailsDestinationId, record to context) + } + private fun onDeviceSelected(device: ServerDevice) { - _state.value = _state.value.copy(deviceName = device.name) startGattClient(device) } @@ -146,100 +151,98 @@ internal class GLSViewModel @Inject constructor( } } - private fun connectDevice(device: ServerDevice) { - repository.downloadData(viewModelScope, device).onEach { - _state.value = WorkingState(it) - - (it as? ConnectedResult)?.let { - analytics.logEvent(ProfileConnectedEvent(Profile.GLS)) - } - }.launchIn(viewModelScope) - } - - private fun startGattClient(blinkyDevice: ServerDevice) = viewModelScope.launch { - client = blinkyDevice.connect(context) + private fun startGattClient(device: ServerDevice) = viewModelScope.launch { + client = device.connect(context) client.connectionState - .onEach { _state.value = _state.value.copy() } .filterNotNull() + .onEach { _state.value = _state.value.copyWithNewConnectionState(it) } .onEach { stopIfDisconnected(it) } + .onEach { logAnalytics(it) } .launchIn(viewModelScope) client.services .filterNotNull() - .onEach { configureGatt(it) } + .onEach { configureGatt(it, device) } .launchIn(viewModelScope) } - private suspend fun configureGatt(services: BleGattServices) { + private fun logAnalytics(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_CONNECTED) { + analytics.logEvent(ProfileConnectedEvent(Profile.GLS)) + } + } + + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val glsService = services.findService(GLS_SERVICE_UUID)!! glucoseMeasurementCharacteristic = glsService.findCharacteristic(GM_CHARACTERISTIC)!! - glucoseMeasurementContextCharacteristic = glsService.findCharacteristic(GM_CONTEXT_CHARACTERISTIC)!! recordAccessControlPointCharacteristic = glsService.findCharacteristic(RACP_CHARACTERISTIC)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } - .onEach { repository.onBatteryLevelChanged(it) } + .onEach { _state.value = _state.value.copyWithNewBatteryLevel(it) } .launchIn(viewModelScope) glucoseMeasurementCharacteristic.getNotifications() .mapNotNull { GlucoseMeasurementParser.parse(it) } - .onEach { } + .onEach { _state.value = _state.value.copyWithNewRecord(it) } .launchIn(viewModelScope) - glucoseMeasurementContextCharacteristic.getNotifications() - .mapNotNull { GlucoseMeasurementContextParser.parse(it) } - .onEach { } - .launchIn(viewModelScope) + glsService.findCharacteristic(GM_CONTEXT_CHARACTERISTIC)?.getNotifications() + ?.mapNotNull { GlucoseMeasurementContextParser.parse(it) } + ?.onEach { _state.value = _state.value.copyWithNewContext(it) } + ?.launchIn(viewModelScope) recordAccessControlPointCharacteristic.getNotifications() .mapNotNull { RecordAccessControlPointParser.parse(it) } .onEach { onAccessControlPointDataReceived(it) } .launchIn(viewModelScope) + + _state.value = _state.value.copy(deviceName = device.name) } private fun stopIfDisconnected(connectionState: GattConnectionState) { if (connectionState == GattConnectionState.STATE_DISCONNECTED) { - stopSelf() + navigationManager.navigateUp() } } - private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) { + private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) = viewModelScope.launch { when (data) { - is NumberOfRecordsData -> if () - is ResponseData -> TODO() - } - if (it.isOperationCompleted && it.wereRecordsFound() && it.numberOfRecords > 0) { - onNumberOfRecordsReceived(it) - } else if (it.isOperationCompleted && it.wereRecordsFound() && it.numberOfRecords == 0) { - onRecordAccessOperationCompletedWithNoRecordsFound(it) - } else if (it.isOperationCompleted && it.wereRecordsFound()) { - onRecordAccessOperationCompleted(it) - } else if (it.errorCode > 0) { - onRecordAccessOperationError(it) + is NumberOfRecordsData -> onNumberOfRecordsReceived(data.numberOfRecords) + is ResponseData -> when (data.responseCode) { + RACPResponseCode.RACP_RESPONSE_SUCCESS -> onRecordAccessOperationCompleted(data.requestCode) + RACPResponseCode.RACP_ERROR_NO_RECORDS_FOUND -> onRecordAccessOperationCompletedWithNoRecordsFound() + RACPResponseCode.RACP_ERROR_OP_CODE_NOT_SUPPORTED, + RACPResponseCode.RACP_ERROR_INVALID_OPERATOR, + RACPResponseCode.RACP_ERROR_OPERATOR_NOT_SUPPORTED, + RACPResponseCode.RACP_ERROR_INVALID_OPERAND, + RACPResponseCode.RACP_ERROR_ABORT_UNSUCCESSFUL, + RACPResponseCode.RACP_ERROR_PROCEDURE_NOT_COMPLETED, + RACPResponseCode.RACP_ERROR_OPERAND_NOT_SUPPORTED -> onRecordAccessOperationError(data.responseCode) + } } } - private fun onRecordAccessOperationCompleted(response: RecordAccessControlPointResponse) { - val status = when (response.requestCode) { - RecordAccessControlPointDataCallback.RACP_OP_CODE_ABORT_OPERATION -> RequestStatus.ABORTED + private fun onRecordAccessOperationCompleted(requestCode: RACPOpCode) { + val status = when (requestCode) { + RACPOpCode.RACP_OP_CODE_ABORT_OPERATION -> RequestStatus.ABORTED else -> RequestStatus.SUCCESS } _state.value = _state.value.copyWithNewRequestStatus(status) } - private fun onRecordAccessOperationCompletedWithNoRecordsFound(response: RecordAccessControlPointResponse) { + private fun onRecordAccessOperationCompletedWithNoRecordsFound() { _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.SUCCESS) } - private suspend fun onNumberOfRecordsReceived(response: RecordAccessControlPointResponse) { - if (response.numberOfRecords > 0) { - if (data.value.records.isNotEmpty()) { - val sequenceNumber = data.value.records.last().sequenceNumber + 1 + private suspend fun onNumberOfRecordsReceived(numberOfRecords: Int) { + if (numberOfRecords > 0) { + if (state.value.glsServiceData.records.isNotEmpty()) { recordAccessControlPointCharacteristic.write( - RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber).value + RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(highestSequenceNumber).value ) } else { recordAccessControlPointCharacteristic.write( @@ -250,8 +253,8 @@ internal class GLSViewModel @Inject constructor( _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.SUCCESS) } - private fun onRecordAccessOperationError(response: RecordAccessControlPointResponse) { - if (response.errorCode == RecordAccessControlPointDataCallback.RACP_ERROR_OP_CODE_NOT_SUPPORTED) { + private fun onRecordAccessOperationError(response: RACPResponseCode) { + if (response == RACPResponseCode.RACP_ERROR_OP_CODE_NOT_SUPPORTED) { _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.NOT_SUPPORTED) } else { _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) @@ -262,21 +265,21 @@ internal class GLSViewModel @Inject constructor( _state.value = _state.value.copyAndClear() } - suspend fun requestLastRecord() { + private suspend fun requestLastRecord() { + clear() + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) - clear() - _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) } - suspend fun requestFirstRecord() { + private suspend fun requestFirstRecord() { + clear() + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) - clear() - _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) } - suspend fun requestAllRecords() { - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) + private suspend fun requestAllRecords() { clear() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index e267a7dc..137cfc4e 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -82,8 +82,8 @@ internal class HRSService : NotificationService() { return START_REDELIVER_INTENT } - private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { - client = blinkyDevice.connect(this@HRSService) + private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { + client = device.connect(this@HRSService) client.connectionState .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index bd308176..f6bd271b 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -80,8 +80,8 @@ internal class HTSService : NotificationService() { return START_REDELIVER_INTENT } - private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { - client = blinkyDevice.connect(this@HTSService) + private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { + client = device.connect(this@HTSService) client.connectionState .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index f1236945..7c907373 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -80,8 +80,8 @@ internal class RSCSService : NotificationService() { return START_REDELIVER_INTENT } - private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { - client = blinkyDevice.connect(this@RSCSService) + private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { + client = device.connect(this@RSCSService) client.connectionState .onEach { repository.onConnectionStateChanged(it) } From 60d41868fbd95f3a694252e4d04749b1f6aad7e4 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 17 Mar 2023 10:28:47 +0100 Subject: [PATCH 015/101] Migrate CGM profile to new BLE library --- profile_cgms/build.gradle.kts | 1 + .../nordicsemi/android/cgms/data/CGMData.kt | 38 --- .../android/cgms/data/CGMManager.kt | 323 ------------------ .../nordicsemi/android/cgms/data/CGMRecord.kt | 42 --- .../android/cgms/data/CGMServiceData.kt | 18 + .../no/nordicsemi/android/cgms/data/Ext.kt | 43 --- .../android/cgms/data/RequestStatus.kt | 36 -- .../android/cgms/repository/CGMRepository.kt | 87 ++--- .../android/cgms/repository/CGMService.kt | 238 ++++++++++++- .../android/cgms/view/CGMContentView.kt | 16 +- .../nordicsemi/android/cgms/view/CGMMapper.kt | 9 +- .../nordicsemi/android/cgms/view/CGMScreen.kt | 42 +-- .../android/cgms/view/CGMViewState.kt | 11 +- .../android/cgms/viewmodel/CGMViewModel.kt | 20 +- .../view/{GLSState.kt => GLSViewState.kt} | 0 .../gls/main/viewmodel/GLSViewModel.kt | 2 +- 16 files changed, 326 insertions(+), 600 deletions(-) delete mode 100644 profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMData.kt delete mode 100644 profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMManager.kt delete mode 100644 profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMRecord.kt create mode 100644 profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMServiceData.kt delete mode 100644 profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/Ext.kt delete mode 100644 profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/RequestStatus.kt rename profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/{GLSState.kt => GLSViewState.kt} (100%) diff --git a/profile_cgms/build.gradle.kts b/profile_cgms/build.gradle.kts index f444d877..204279e2 100644 --- a/profile_cgms/build.gradle.kts +++ b/profile_cgms/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(libs.nordic.theme) implementation(libs.nordic.uiscanner) implementation(libs.nordic.navigation) + implementation(libs.nordic.core) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMData.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMData.kt deleted file mode 100644 index a0764930..00000000 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMData.kt +++ /dev/null @@ -1,38 +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.data - -internal data class CGMData( - val records: List = emptyList(), - val batteryLevel: Int? = null, - val requestStatus: RequestStatus = RequestStatus.IDLE -) diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMManager.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMManager.kt deleted file mode 100644 index 55c1189e..00000000 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMManager.kt +++ /dev/null @@ -1,323 +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.data - -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic -import android.content.Context -import android.util.Log -import android.util.SparseArray -import kotlinx.coroutines.CoroutineScope -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.RecordAccessControlPointResponse -import no.nordicsemi.android.ble.common.callback.battery.BatteryLevelResponse -import no.nordicsemi.android.ble.common.callback.cgm.CGMFeatureResponse -import no.nordicsemi.android.ble.common.callback.cgm.CGMSpecificOpsControlPointResponse -import no.nordicsemi.android.ble.common.callback.cgm.CGMStatusResponse -import no.nordicsemi.android.ble.common.callback.cgm.ContinuousGlucoseMeasurementResponse -import no.nordicsemi.android.ble.common.data.RecordAccessControlPointData -import no.nordicsemi.android.ble.common.data.cgm.CGMSpecificOpsControlPointData -import no.nordicsemi.android.ble.common.profile.RecordAccessControlPointCallback -import no.nordicsemi.android.ble.common.profile.cgm.CGMSpecificOpsControlPointCallback -import no.nordicsemi.android.ble.ktx.asValidResponseFlow -import no.nordicsemi.android.ble.ktx.suspend -import no.nordicsemi.android.ble.ktx.suspendForValidResponse -import no.nordicsemi.android.common.logger.NordicLogger -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") -private val CGM_STATUS_UUID = UUID.fromString("00002AA9-0000-1000-8000-00805f9b34fb") -private val CGM_FEATURE_UUID = UUID.fromString("00002AA8-0000-1000-8000-00805f9b34fb") -private val CGM_MEASUREMENT_UUID = UUID.fromString("00002AA7-0000-1000-8000-00805f9b34fb") -private val CGM_OPS_CONTROL_POINT_UUID = UUID.fromString("00002AAC-0000-1000-8000-00805f9b34fb") - -private val RACP_UUID = UUID.fromString("00002A52-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 CGMManager( - context: Context, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : BleManager(context) { - - private var cgmStatusCharacteristic: BluetoothGattCharacteristic? = null - private var cgmFeatureCharacteristic: BluetoothGattCharacteristic? = null - private var cgmMeasurementCharacteristic: BluetoothGattCharacteristic? = null - private var cgmSpecificOpsControlPointCharacteristic: BluetoothGattCharacteristic? = null - private var recordAccessControlPointCharacteristic: BluetoothGattCharacteristic? = null - private val records: SparseArray = SparseArray() - private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null - - private var secured = false - - private var recordAccessRequestInProgress = false - - private var sessionStartTime: Long = 0 - - private val data = MutableStateFlow(CGMData()) - val dataHolder = ConnectionObserverAdapter() - - init { - connectionObserver = dataHolder - - data.onEach { - dataHolder.setValue(it) - }.launchIn(scope) - } - - override fun getGattCallback(): BleManagerGattCallback { - return CGMManagerGattCallback() - } - - override fun log(priority: Int, message: String) { - logger.log(priority, message) - } - - override fun getMinLogPriority(): Int { - return Log.VERBOSE - } - - private inner class CGMManagerGattCallback : BleManagerGattCallback() { - override fun initialize() { - super.initialize() - - setNotificationCallback(cgmMeasurementCharacteristic).asValidResponseFlow() - .onEach { - if (sessionStartTime == 0L && !recordAccessRequestInProgress) { - val timeOffset = it.items.minOf { it.timeOffset } - sessionStartTime = System.currentTimeMillis() - timeOffset * 60000L - } - - it.items.map { - val timestamp = sessionStartTime + it.timeOffset * 60000L - val item = CGMRecord(it.timeOffset, it.glucoseConcentration, timestamp) - records.put(item.sequenceNumber, item) - } - - data.value = data.value.copy(records = records.toList()) - }.launchIn(scope) - - setIndicationCallback(cgmSpecificOpsControlPointCharacteristic).asValidResponseFlow() - .onEach { - if (it.isOperationCompleted) { - when (it.requestCode) { - CGMSpecificOpsControlPointCallback.CGM_OP_CODE_START_SESSION -> sessionStartTime = - System.currentTimeMillis() - CGMSpecificOpsControlPointCallback.CGM_OP_CODE_STOP_SESSION -> sessionStartTime = - 0 - } - } else { - when (it.requestCode) { - CGMSpecificOpsControlPointCallback.CGM_OP_CODE_START_SESSION -> - if (it.errorCode == CGMSpecificOpsControlPointCallback.CGM_ERROR_PROCEDURE_NOT_COMPLETED) { - sessionStartTime = 0 - } - CGMSpecificOpsControlPointCallback.CGM_OP_CODE_STOP_SESSION -> sessionStartTime = - 0 - } - } - }.launchIn(scope) - - setIndicationCallback(recordAccessControlPointCharacteristic).asValidResponseFlow() - .onEach { - if (it.isOperationCompleted && it.wereRecordsFound() && it.numberOfRecords > 0) { - onRecordsReceived(it) - } else if (it.isOperationCompleted && !it.wereRecordsFound()) { - onNoRecordsFound() - } else if (it.isOperationCompleted && it.wereRecordsFound()) { - onOperationCompleted(it) - } else if (it.errorCode > 0) { - onError(it) - } - }.launchIn(scope) - - setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow() - .onEach { - 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() - this@CGMManager.secured = cgmResponse.features.e2eCrcSupported - } - - scope.launchWithCatch { - val response = readCharacteristic(cgmStatusCharacteristic).suspendForValidResponse() - if (response.status?.sessionStopped == false) { - sessionStartTime = System.currentTimeMillis() - response.timeOffset * 60000L - } - } - - scope.launchWithCatch { - if (sessionStartTime == 0L) { - writeCharacteristic( - cgmSpecificOpsControlPointCharacteristic, - CGMSpecificOpsControlPointData.startSession(secured), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } - } - } - - private suspend fun onRecordsReceived(response: RecordAccessControlPointResponse) { - if (response.numberOfRecords > 0) { - if (records.size() > 0) { - val sequenceNumber = records.keyAt(records.size() - 1) + 1 - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo( - sequenceNumber - ), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } else { - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportAllStoredRecords(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } - } else { - recordAccessRequestInProgress = false - data.value = data.value.copy(requestStatus = RequestStatus.SUCCESS) - } - } - - private fun onNoRecordsFound() { - recordAccessRequestInProgress = false - data.value = data.value.copy(requestStatus = RequestStatus.SUCCESS) - } - - private fun onOperationCompleted(response: RecordAccessControlPointResponse) { - when (response.requestCode) { - RecordAccessControlPointCallback.RACP_OP_CODE_ABORT_OPERATION -> - data.value = data.value.copy(requestStatus = RequestStatus.ABORTED) - else -> { - recordAccessRequestInProgress = false - data.value = data.value.copy(requestStatus = RequestStatus.SUCCESS) - } - } - } - - private fun onError(response: RecordAccessControlPointResponse) { - if (response.errorCode == RecordAccessControlPointCallback.RACP_ERROR_OP_CODE_NOT_SUPPORTED) { - data.value = data.value.copy(requestStatus = RequestStatus.NOT_SUPPORTED) - } else { - data.value = data.value.copy(requestStatus = RequestStatus.FAILED) - } - } - - override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - gatt.getService(CGMS_SERVICE_UUID)?.run { - cgmStatusCharacteristic = getCharacteristic(CGM_STATUS_UUID) - cgmFeatureCharacteristic = getCharacteristic(CGM_FEATURE_UUID) - cgmMeasurementCharacteristic = getCharacteristic(CGM_MEASUREMENT_UUID) - cgmSpecificOpsControlPointCharacteristic = getCharacteristic(CGM_OPS_CONTROL_POINT_UUID) - recordAccessControlPointCharacteristic = getCharacteristic(RACP_UUID) - } - gatt.getService(BATTERY_SERVICE_UUID)?.run { - batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID) - } - return cgmMeasurementCharacteristic != null - && cgmSpecificOpsControlPointCharacteristic != null - && recordAccessControlPointCharacteristic != null - && cgmStatusCharacteristic != null - && cgmFeatureCharacteristic != null - } - - override fun onServicesInvalidated() { - cgmStatusCharacteristic = null - cgmFeatureCharacteristic = null - cgmMeasurementCharacteristic = null - cgmSpecificOpsControlPointCharacteristic = null - recordAccessControlPointCharacteristic = null - batteryLevelCharacteristic = null - } - } - - private fun clear() { - records.clear() - } - - fun requestLastRecord() { - if (recordAccessControlPointCharacteristic == null) return - clear() - data.value = data.value.copy(requestStatus = RequestStatus.PENDING) - recordAccessRequestInProgress = true - scope.launchWithCatch { - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportLastStoredRecord(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } - } - - fun requestFirstRecord() { - if (recordAccessControlPointCharacteristic == null) return - clear() - data.value = data.value.copy(requestStatus = RequestStatus.PENDING) - recordAccessRequestInProgress = true - scope.launchWithCatch { - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportFirstStoredRecord(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } - } - - fun requestAllRecords() { - if (recordAccessControlPointCharacteristic == null) return - clear() - data.value = data.value.copy(requestStatus = RequestStatus.PENDING) - recordAccessRequestInProgress = true - scope.launchWithCatch { - writeCharacteristic( - recordAccessControlPointCharacteristic, - RecordAccessControlPointData.reportNumberOfAllStoredRecords(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).suspend() - } - } -} diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMRecord.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMRecord.kt deleted file mode 100644 index 3bcef7e0..00000000 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMRecord.kt +++ /dev/null @@ -1,42 +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.data - -import android.os.Parcelable -import kotlinx.parcelize.Parcelize - -@Parcelize -internal data class CGMRecord( - var sequenceNumber: Int, - var glucoseConcentration: Float, - var timestamp: Long -) : Parcelable 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 new file mode 100644 index 00000000..d3f20a28 --- /dev/null +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/CGMServiceData.kt @@ -0,0 +1,18 @@ +package no.nordicsemi.android.cgms.data + +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMRecord +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus + +internal data class CGMServiceData( + val records: List = emptyList(), + val batteryLevel: Int? = null, + val connectionState: GattConnectionState? = null, + val requestStatus: RequestStatus = RequestStatus.IDLE +) + +data class CGMRecordWithSequenceNumber( + val sequenceNumber: Int, + val record: CGMRecord, + val timestamp: Long +) diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/Ext.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/Ext.kt deleted file mode 100644 index 80861747..00000000 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/Ext.kt +++ /dev/null @@ -1,43 +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.data - -import android.util.SparseArray -import androidx.core.util.keyIterator - -internal fun SparseArray.toList(): List { - val list = mutableListOf() - this.keyIterator().forEach { - list.add(get(it)) - } - return list.sortedBy { it.sequenceNumber }.toList() -} diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/RequestStatus.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/RequestStatus.kt deleted file mode 100644 index 6ded6128..00000000 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/data/RequestStatus.kt +++ /dev/null @@ -1,36 +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.data - -internal enum class RequestStatus { - IDLE, PENDING, SUCCESS, ABORTED, FAILED, NOT_SUPPORTED -} 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 f76367d9..98117309 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 @@ -33,23 +33,19 @@ package no.nordicsemi.android.cgms.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import no.nordicsemi.android.ble.ktx.suspend -import no.nordicsemi.android.cgms.data.CGMData -import no.nordicsemi.android.cgms.data.CGMManager -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory +import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber +import no.nordicsemi.android.cgms.data.CGMServiceCommand +import no.nordicsemi.android.cgms.data.CGMServiceData +import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.service.IdleResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus +import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -58,68 +54,53 @@ class CGMRepository @Inject constructor( @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst ) { - private var manager: CGMManager? = null - private var logger: NordicLogger? = null - - private val _data = MutableStateFlow>(IdleResult()) + private val _data = MutableStateFlow(CGMServiceData()) internal val data = _data.asStateFlow() - val isRunning = data.map { it.isRunning() } - val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } + private val _stopEvent = simpleSharedFlow() + internal val stopEvent = _stopEvent.asSharedFlow() + + private val _command = simpleSharedFlow() + internal val command = _command.asSharedFlow() + + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } + val hasRecords = data.value.records.isNotEmpty() + val highestSequenceNumber = data.value.records.maxOfOrNull { it.sequenceNumber } ?: -1 fun launch(device: ServerDevice) { serviceManager.startService(CGMService::class.java, device) } - fun start(device: ServerDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CGMS", device.address).also { - logger = it - } - val manager = CGMManager(context, scope, createdLogger) - this.manager = manager - - manager.dataHolder.status.onEach { - _data.value = it - }.launchIn(scope) - - scope.launch { - manager.start(device) - } + fun onDataReceived(data: List) { + _data.value = _data.value.copy(records = _data.value.records + data) } - private suspend fun CGMManager.start(device: ServerDevice) { -// try { -// connect(device.device) -// .useAutoConnect(false) -// .retry(3, 100) -// .suspend() -// } catch (e: Exception) { -// e.printStackTrace() -// } + internal fun onCommand(command: CGMServiceCommand) { + _command.tryEmit(command) } - fun requestAllRecords() { - manager?.requestAllRecords() + fun onConnectionStateChanged(connectionState: GattConnectionState?) { + _data.value = _data.value.copy(connectionState = connectionState) } - fun requestLastRecord() { - manager?.requestLastRecord() + fun onBatteryLevelChanged(batteryLevel: Int) { + _data.value = _data.value.copy(batteryLevel = batteryLevel) } - fun requestFirstRecord() { - manager?.requestFirstRecord() + fun onNewRequestStatus(requestStatus: RequestStatus) { + _data.value = _data.value.copy(requestStatus = requestStatus) } fun openLogger() { - NordicLogger.launch(context, logger) + TODO() + } + + fun clear() { + _data.value = _data.value.copy(records = emptyList()) } fun release() { - manager?.disconnect()?.enqueue() - logger = null - manager = null + _stopEvent.tryEmit(DisconnectAndStopEvent()) } } 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 9f53dc2d..aa308938 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 @@ -31,33 +31,263 @@ package no.nordicsemi.android.cgms.repository +import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import no.nordicsemi.android.ble.common.data.cgm.CGMSpecificOpsControlPointData +import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber +import no.nordicsemi.android.cgms.data.CGMServiceCommand import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.cgm.CGMFeatureParser +import no.nordicsemi.android.kotlin.ble.profile.cgm.CGMMeasurementParser +import no.nordicsemi.android.kotlin.ble.profile.cgm.CGMSpecificOpsControlPointParser +import no.nordicsemi.android.kotlin.ble.profile.cgm.CGMStatusParser +import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMErrorCode +import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMOpCode +import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser +import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointParser +import no.nordicsemi.android.kotlin.ble.profile.gls.data.NumberOfRecordsData +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RecordAccessControlPointData +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus +import no.nordicsemi.android.kotlin.ble.profile.gls.data.ResponseData +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 java.util.* import javax.inject.Inject +val CGMS_SERVICE_UUID: UUID = UUID.fromString("0000181F-0000-1000-8000-00805f9b34fb") +private val CGM_STATUS_UUID = UUID.fromString("00002AA9-0000-1000-8000-00805f9b34fb") +private val CGM_FEATURE_UUID = UUID.fromString("00002AA8-0000-1000-8000-00805f9b34fb") +private val CGM_MEASUREMENT_UUID = UUID.fromString("00002AA7-0000-1000-8000-00805f9b34fb") +private val CGM_OPS_CONTROL_POINT_UUID = UUID.fromString("00002AAC-0000-1000-8000-00805f9b34fb") + +private val RACP_UUID = UUID.fromString("00002A52-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") + +@SuppressLint("MissingPermission") @AndroidEntryPoint internal class CGMService : NotificationService() { @Inject lateinit var repository: CGMRepository + private lateinit var client: BleGattClient + + private var secured = false + + private var recordAccessRequestInProgress = false + + private var sessionStartTime: Long = 0 + + private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) val device = intent!!.getParcelableExtra(DEVICE_DATA)!! - repository.start(device, lifecycleScope) + startGattClient(device) - repository.hasBeenDisconnected.onEach { - if (it) stopSelf() - }.launchIn(lifecycleScope) + repository.stopEvent + .onEach { disconnect() } + .launchIn(lifecycleScope) + + repository.command + .onEach { onCommand(it) } + .launchIn(lifecycleScope) return START_REDELIVER_INTENT } + + private fun onCommand(command: CGMServiceCommand) = lifecycleScope.launch{ + when (command) { + CGMServiceCommand.REQUEST_ALL_RECORDS -> requestAllRecords() + CGMServiceCommand.REQUEST_LAST_RECORD -> requestLastRecord() + CGMServiceCommand.REQUEST_FIRST_RECORD -> requestFirstRecord() + CGMServiceCommand.DISCONNECT -> client.disconnect() + } + } + + private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { + client = device.connect(this@CGMService) + + client.connectionState + .onEach { repository.onConnectionStateChanged(it) } + .filterNotNull() + .onEach { stopIfDisconnected(it) } + .launchIn(lifecycleScope) + + client.services + .filterNotNull() + .onEach { configureGatt(it) } + .launchIn(lifecycleScope) + } + + 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 batteryService = services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + batteryLevelCharacteristic.getNotifications() + .mapNotNull { BatteryLevelParser.parse(it) } + .onEach { repository.onBatteryLevelChanged(it) } + .launchIn(lifecycleScope) + + measurementCharacteristic.getNotifications() + .mapNotNull { CGMMeasurementParser.parse(it) } + .onEach { + if (sessionStartTime == 0L && !recordAccessRequestInProgress) { + val timeOffset = it.minOf { it.timeOffset } + sessionStartTime = System.currentTimeMillis() - timeOffset * 60000L + } + + val result = it.map { + val timestamp = sessionStartTime + it.timeOffset * 60000L + CGMRecordWithSequenceNumber(it.timeOffset, it, timestamp) + } + + repository.onDataReceived(result) + } + .launchIn(lifecycleScope) + + opsControlPointCharacteristic.getNotifications() + .mapNotNull { CGMSpecificOpsControlPointParser.parse(it) } + .onEach { + if (it.isOperationCompleted) { + if (it.requestCode == CGMOpCode.CGM_OP_CODE_START_SESSION) { + sessionStartTime = System.currentTimeMillis() + } else { + sessionStartTime = 0 + } + } else { + if (it.requestCode == CGMOpCode.CGM_OP_CODE_START_SESSION && it.errorCode == CGMErrorCode.CGM_ERROR_PROCEDURE_NOT_COMPLETED) { + sessionStartTime = 0 + } else if (it.requestCode == CGMOpCode.CGM_OP_CODE_STOP_SESSION) { + sessionStartTime = 0 + } + } + } + .launchIn(lifecycleScope) + + recordAccessControlPointCharacteristic.getNotifications() + .mapNotNull { RecordAccessControlPointParser.parse(it) } + .onEach { onAccessControlPointDataReceived(it) } + .launchIn(lifecycleScope) + + 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 + } + + if (sessionStartTime == 0L) { + opsControlPointCharacteristic.write(CGMSpecificOpsControlPointData.startSession(secured).value!!) + } + } + + private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) = lifecycleScope.launch { + when (data) { + is NumberOfRecordsData -> onNumberOfRecordsReceived(data.numberOfRecords) + is ResponseData -> when (data.responseCode) { + RACPResponseCode.RACP_RESPONSE_SUCCESS -> onRecordAccessOperationCompleted(data.requestCode) + RACPResponseCode.RACP_ERROR_NO_RECORDS_FOUND -> onRecordAccessOperationCompletedWithNoRecordsFound() + RACPResponseCode.RACP_ERROR_OP_CODE_NOT_SUPPORTED, + RACPResponseCode.RACP_ERROR_INVALID_OPERATOR, + RACPResponseCode.RACP_ERROR_OPERATOR_NOT_SUPPORTED, + RACPResponseCode.RACP_ERROR_INVALID_OPERAND, + RACPResponseCode.RACP_ERROR_ABORT_UNSUCCESSFUL, + RACPResponseCode.RACP_ERROR_PROCEDURE_NOT_COMPLETED, + RACPResponseCode.RACP_ERROR_OPERAND_NOT_SUPPORTED -> onRecordAccessOperationError(data.responseCode) + } + } + } + + private fun onRecordAccessOperationCompleted(requestCode: RACPOpCode) { + val status = when (requestCode) { + RACPOpCode.RACP_OP_CODE_ABORT_OPERATION -> RequestStatus.ABORTED + else -> RequestStatus.SUCCESS + } + repository.onNewRequestStatus(status) + } + + private fun onRecordAccessOperationCompletedWithNoRecordsFound() { + repository.onNewRequestStatus(RequestStatus.SUCCESS) + } + + private suspend fun onNumberOfRecordsReceived(numberOfRecords: Int) { + if (numberOfRecords > 0) { + if (repository.hasRecords) { + recordAccessControlPointCharacteristic.write( + RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(repository.highestSequenceNumber).value + ) + } else { + recordAccessControlPointCharacteristic.write( + RecordAccessControlPointInputParser.reportAllStoredRecords().value + ) + } + } + repository.onNewRequestStatus(RequestStatus.SUCCESS) + } + + private fun onRecordAccessOperationError(response: RACPResponseCode) { + if (response == RACPResponseCode.RACP_ERROR_OP_CODE_NOT_SUPPORTED) { + repository.onNewRequestStatus(RequestStatus.NOT_SUPPORTED) + } else { + repository.onNewRequestStatus(RequestStatus.FAILED) + } + } + + private fun clear() { + repository.clear() + } + + private suspend fun requestLastRecord() { + clear() + repository.onNewRequestStatus(RequestStatus.PENDING) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) + } + + private suspend fun requestFirstRecord() { + clear() + repository.onNewRequestStatus(RequestStatus.PENDING) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) + } + + private suspend fun requestAllRecords() { + clear() + repository.onNewRequestStatus(RequestStatus.PENDING) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) + } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + stopSelf() + } + } + + private fun disconnect() { + client.disconnect() + } } diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMContentView.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMContentView.kt index bdfacfc0..b2449b7f 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMContentView.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMContentView.kt @@ -52,16 +52,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import no.nordicsemi.android.cgms.R -import no.nordicsemi.android.cgms.data.CGMData -import no.nordicsemi.android.cgms.data.CGMRecord +import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber import no.nordicsemi.android.cgms.data.CGMServiceCommand -import no.nordicsemi.android.cgms.data.RequestStatus +import no.nordicsemi.android.cgms.data.CGMServiceData +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun CGMContentView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) { +internal fun CGMContentView(state: CGMServiceData, onEvent: (CGMViewEvent) -> Unit) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally @@ -91,7 +91,7 @@ internal fun CGMContentView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) { } @Composable -private fun SettingsView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) { +private fun SettingsView(state: CGMServiceData, onEvent: (CGMViewEvent) -> Unit) { ScreenSection { SectionTitle(icon = Icons.Default.Settings, title = "Request items") @@ -119,7 +119,7 @@ private fun SettingsView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) { } @Composable -private fun RecordsView(state: CGMData) { +private fun RecordsView(state: CGMServiceData) { ScreenSection { if (state.records.isEmpty()) { RecordsViewWithoutData() @@ -131,7 +131,7 @@ private fun RecordsView(state: CGMData) { } @Composable -private fun RecordsViewWithData(state: CGMData) { +private fun RecordsViewWithData(state: CGMServiceData) { Column(modifier = Modifier.fillMaxWidth()) { SectionTitle(resId = R.drawable.ic_records, title = "Records") @@ -148,7 +148,7 @@ private fun RecordsViewWithData(state: CGMData) { } @Composable -private fun RecordItem(record: CGMRecord) { +private fun RecordItem(record: CGMRecordWithSequenceNumber) { Row(verticalAlignment = Alignment.CenterVertically) { Column( modifier = Modifier diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMMapper.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMMapper.kt index 94dbf67a..f95c98af 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMMapper.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMMapper.kt @@ -34,16 +34,17 @@ package no.nordicsemi.android.cgms.view import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import no.nordicsemi.android.cgms.R -import no.nordicsemi.android.cgms.data.CGMRecord +import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber +import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMRecord import java.text.SimpleDateFormat import java.util.* -internal fun CGMRecord.formattedTime(): String { +internal fun CGMRecordWithSequenceNumber.formattedTime(): String { val timeFormat = SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.US) return timeFormat.format(Date(timestamp)) } @Composable -internal fun CGMRecord.glucoseConcentration(): String { - return stringResource(id = R.string.cgms_value_unit, glucoseConcentration) +internal fun CGMRecordWithSequenceNumber.glucoseConcentration(): String { + return stringResource(id = R.string.cgms_value_unit, record.glucoseConcentration) } 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 384295e0..8efabf69 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 @@ -48,15 +48,7 @@ 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 import no.nordicsemi.android.common.ui.scanner.view.Reason -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult -import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -78,17 +70,15 @@ fun CGMScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state) { - NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.result) { - is IdleResult, - is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) } - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) } - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - is SuccessResult -> CGMContentView(state.result.data) { viewModel.onEvent(it) } + if (state.deviceName == null) { + DeviceConnectingView() + } else { + when (state.result?.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) } } } } @@ -97,15 +87,11 @@ fun CGMScreen() { @Composable private fun AppBar(state: CGMViewState, navigateUp: () -> Unit, viewModel: CGMViewModel) { - val toolbarName = (state as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { - BackIconAppBar(stringResource(id = R.string.cgms_title), navigateUp) - } else { - LoggerIconAppBar(toolbarName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { + if (state.deviceName?.isNotBlank() == true) { + LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) } + } else { + BackIconAppBar(stringResource(id = R.string.cgms_title), navigateUp) } } 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 index b53cc4f6..842f35f7 100644 --- 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 @@ -31,10 +31,9 @@ package no.nordicsemi.android.cgms.view -import no.nordicsemi.android.cgms.data.CGMData -import no.nordicsemi.android.service.BleManagerResult +import no.nordicsemi.android.cgms.data.CGMServiceData -internal sealed class CGMViewState - -internal data class WorkingState(val result: BleManagerResult) : CGMViewState() -internal object NoDeviceState : CGMViewState() +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 afca2922..ac340591 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 @@ -44,21 +44,19 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent -import no.nordicsemi.android.cgms.data.CGMS_SERVICE_UUID 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.NoDeviceState import no.nordicsemi.android.cgms.view.OnWorkingModeSelected import no.nordicsemi.android.cgms.view.OpenLoggerEvent -import no.nordicsemi.android.cgms.view.WorkingState import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.ConnectedResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -69,7 +67,7 @@ internal class CGMViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(NoDeviceState) + private val _state = MutableStateFlow(CGMViewState()) val state = _state.asStateFlow() init { @@ -80,9 +78,9 @@ internal class CGMViewModel @Inject constructor( } repository.data.onEach { - _state.value = WorkingState(it) + _state.value = _state.value.copy(result = it) - (it as? ConnectedResult)?.let { + if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.CGMS)) } }.launchIn(viewModelScope) @@ -113,16 +111,10 @@ internal class CGMViewModel @Inject constructor( } private fun onCommandReceived(workingMode: CGMServiceCommand) { - when (workingMode) { - CGMServiceCommand.REQUEST_ALL_RECORDS -> repository.requestAllRecords() - CGMServiceCommand.REQUEST_LAST_RECORD -> repository.requestLastRecord() - CGMServiceCommand.REQUEST_FIRST_RECORD -> repository.requestFirstRecord() - CGMServiceCommand.DISCONNECT -> disconnect() - } + repository.onCommand(workingMode) } private fun disconnect() { repository.release() - navigationManager.navigateUp() } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt similarity index 100% rename from profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSState.kt rename to profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index c48be0b8..14feaa78 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -200,7 +200,7 @@ internal class GLSViewModel @Inject constructor( .onEach { onAccessControlPointDataReceived(it) } .launchIn(viewModelScope) - _state.value = _state.value.copy(deviceName = device.name) + _state.value = _state.value.copy(deviceName = device.name) //prevents UI from appearing before BLE connection is set up } private fun stopIfDisconnected(connectionState: GattConnectionState) { From ccef26ae096b7df4c4d18c30df02834a1b9374a2 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 20 Mar 2023 09:18:12 +0100 Subject: [PATCH 016/101] Fix CGM profile --- .../java/no/nordicsemi/android/utils/Ext.kt | 3 +- .../android/cgms/data/CGMServiceData.kt | 3 +- .../android/cgms/repository/CGMRepository.kt | 1 + .../android/cgms/repository/CGMService.kt | 27 +++++++------ .../nordicsemi/android/cgms/view/CGMScreen.kt | 7 ++-- .../android/cgms/view/CGMViewState.kt | 39 ------------------- .../android/cgms/viewmodel/CGMViewModel.kt | 12 +++--- 7 files changed, 31 insertions(+), 61 deletions(-) delete mode 100644 profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMViewState.kt 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) } From 488104c3d5f0691f3c2e9190c31d5208c72fb030 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 20 Mar 2023 13:43:49 +0100 Subject: [PATCH 017/101] Fix CSC profile --- .../android/csc/data/CSCServiceData.kt | 6 ++- .../android/csc/{view => data}/SpeedUnit.kt | 2 +- .../android/csc/repository/CSCRepository.kt | 9 ++++ .../android/csc/repository/CSCService.kt | 6 ++- .../android/csc/view/CSCContentView.kt | 9 ++-- .../nordicsemi/android/csc/view/CSCMappers.kt | 1 + .../nordicsemi/android/csc/view/CSCScreen.kt | 12 +++-- .../nordicsemi/android/csc/view/CSCState.kt | 46 ------------------- .../android/csc/view/CSCViewEvent.kt | 1 + .../android/csc/view/SensorsReadingView.kt | 1 + .../android/csc/viewmodel/CSCViewModel.kt | 14 ++---- 11 files changed, 36 insertions(+), 71 deletions(-) rename profile_csc/src/main/java/no/nordicsemi/android/csc/{view => data}/SpeedUnit.kt (97%) delete mode 100644 profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt index d0a4af36..635fb318 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt @@ -3,8 +3,10 @@ package no.nordicsemi.android.csc.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData -data class CSCServiceData( +internal data class CSCServiceData( val data: CSCData = CSCData(), val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null + val connectionState: GattConnectionState? = null, + val speedUnit: SpeedUnit = SpeedUnit.M_S, + val deviceName: String? = null ) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SpeedUnit.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/SpeedUnit.kt similarity index 97% rename from profile_csc/src/main/java/no/nordicsemi/android/csc/view/SpeedUnit.kt rename to profile_csc/src/main/java/no/nordicsemi/android/csc/data/SpeedUnit.kt index fe083614..0ebea419 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SpeedUnit.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/SpeedUnit.kt @@ -29,7 +29,7 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.csc.view +package no.nordicsemi.android.csc.data internal enum class SpeedUnit(val displayName: String) { M_S("m/s"), diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index a149395c..db1507b5 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.csc.data.CSCServiceData +import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData @@ -73,6 +74,14 @@ class CSCRepository @Inject constructor( serviceManager.startService(CSCService::class.java, device) } + fun onInitComplete(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) + } + + internal fun setSpeedUnit(speedUnit: SpeedUnit) { + _data.value = _data.value.copy(speedUnit = speedUnit) + } + fun setWheelSize(wheelSize: WheelSize) { _wheelSize.value = wheelSize } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index f9d0f303..181abbd0 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -91,11 +91,11 @@ internal class CSCService : NotificationService() { client.services .filterNotNull() - .onEach { configureGatt(it) } + .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val cscService = services.findService(CSC_SERVICE_UUID)!! val cscMeasurementCharacteristic = cscService.findCharacteristic(CSC_MEASUREMENT_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! @@ -111,6 +111,8 @@ internal class CSCService : NotificationService() { .mapNotNull { cscDataParser.parse(it, repository.wheelSize.value) } .onEach { repository.onCSCDataChanged(it) } .launchIn(lifecycleScope) + + repository.onInitComplete(device) } private fun stopIfDisconnected(connectionState: GattConnectionState) { diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt index a970e2f8..2140926a 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt @@ -50,6 +50,7 @@ import androidx.compose.ui.unit.dp import no.nordicsemi.android.common.theme.view.RadioButtonGroup import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.data.CSCServiceData +import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSize import no.nordicsemi.android.ui.view.ScreenSection @@ -58,7 +59,7 @@ import no.nordicsemi.android.ui.view.dialog.FlowCanceled import no.nordicsemi.android.ui.view.dialog.ItemSelectedResult @Composable -internal fun CSCContentView(state: CSCServiceData, speedUnit: SpeedUnit, onEvent: (CSCViewEvent) -> Unit) { +internal fun CSCContentView(state: CSCServiceData, onEvent: (CSCViewEvent) -> Unit) { val showDialog = rememberSaveable { mutableStateOf(false) } if (showDialog.value) { @@ -79,11 +80,11 @@ internal fun CSCContentView(state: CSCServiceData, speedUnit: SpeedUnit, onEvent Column( horizontalAlignment = Alignment.CenterHorizontally, ) { - SettingsSection(state.data, speedUnit, onEvent) { showDialog.value = true } + SettingsSection(state.data, state.speedUnit, onEvent) { showDialog.value = true } Spacer(modifier = Modifier.height(16.dp)) - SensorsReadingView(state = state, speedUnit) + SensorsReadingView(state = state, state.speedUnit) Spacer(modifier = Modifier.height(16.dp)) @@ -125,5 +126,5 @@ private fun SettingsSection( @Preview @Composable private fun ConnectedPreview() { - CSCContentView(CSCServiceData(), SpeedUnit.KM_H) { } + CSCContentView(CSCServiceData()) { } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt index 660972c4..e60e3f30 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt @@ -33,6 +33,7 @@ package no.nordicsemi.android.csc.view import no.nordicsemi.android.common.theme.view.RadioButtonItem import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity +import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData import java.util.* diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 003d895b..692ba72d 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -47,6 +47,7 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.csc.R +import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.viewmodel.CSCViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar @@ -70,14 +71,15 @@ fun CSCScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.cscManagerState) { - NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.cscManagerState.result.connectionState) { + if (state.deviceName == null) { + DeviceConnectingView() + } else { + when (state.connectionState) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> CSCContentView(state.cscManagerState.result, state.speedUnit) { viewModel.onEvent(it) } + GattConnectionState.STATE_CONNECTED -> CSCContentView(state) { viewModel.onEvent(it) } } } } @@ -85,7 +87,7 @@ fun CSCScreen() { } @Composable -private fun AppBar(state: CSCViewState, navigateUp: () -> Unit, viewModel: CSCViewModel) { +private fun AppBar(state: CSCServiceData, navigateUp: () -> Unit, viewModel: CSCViewModel) { if (state.deviceName?.isNotBlank() == true) { LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(OnDisconnectButtonClick) }) { viewModel.onEvent(OpenLogger) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt deleted file mode 100644 index c5638323..00000000 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCState.kt +++ /dev/null @@ -1,46 +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.csc.view - -import no.nordicsemi.android.csc.data.CSCServiceData - -internal data class CSCViewState( - val speedUnit: SpeedUnit = SpeedUnit.M_S, - val cscManagerState: CSCMangerState = NoDeviceState, - val deviceName: String? = null -) - -internal sealed class CSCMangerState - -internal data class WorkingState(val result: CSCServiceData) : CSCMangerState() - -internal object NoDeviceState : CSCMangerState() diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt index 87f510db..d48b4e7e 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewEvent.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.csc.view +import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSize internal sealed class CSCViewEvent diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt index 10cbbd5c..3bbba565 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt @@ -41,6 +41,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.data.CSCServiceData +import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index b6a0c75e..19fb8c6c 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -46,17 +44,15 @@ import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator +import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.csc.repository.CSCRepository import no.nordicsemi.android.csc.repository.CSC_SERVICE_UUID import no.nordicsemi.android.csc.view.CSCViewEvent -import no.nordicsemi.android.csc.view.CSCViewState import no.nordicsemi.android.csc.view.NavigateUp 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.csc.view.OpenLogger -import no.nordicsemi.android.csc.view.SpeedUnit -import no.nordicsemi.android.csc.view.WorkingState import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId @@ -69,8 +65,7 @@ internal class CSCViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(CSCViewState()) - val state = _state.asStateFlow() + val state = repository.data init { viewModelScope.launch { @@ -80,8 +75,6 @@ internal class CSCViewModel @Inject constructor( } repository.data.onEach { - _state.value = _state.value.copy(cscManagerState = WorkingState(it)) - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.CSC)) } @@ -104,7 +97,6 @@ internal class CSCViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _state.value = _state.value.copy(deviceName = device.name) repository.launch(device) } @@ -119,7 +111,7 @@ internal class CSCViewModel @Inject constructor( } private fun setSpeedUnit(speedUnit: SpeedUnit) { - _state.value = _state.value.copy(speedUnit = speedUnit) + repository.setSpeedUnit(speedUnit) } private fun disconnect() { From 33bc7cba6db0fb6ebec61a8ca7e54b8e1d331c98 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 20 Mar 2023 13:49:58 +0100 Subject: [PATCH 018/101] Fix HRS profile --- .../android/hrs/data/HRSServiceData.kt | 4 +- .../android/hrs/service/HRSRepository.kt | 8 ++++ .../android/hrs/service/HRSService.kt | 6 ++- .../android/hrs/view/HRSContentView.kt | 8 ++-- .../nordicsemi/android/hrs/view/HRSScreen.kt | 12 +++-- .../nordicsemi/android/hrs/view/HRSState.kt | 46 ------------------- .../android/hrs/viewmodel/HRSViewModel.kt | 12 +---- 7 files changed, 28 insertions(+), 68 deletions(-) delete mode 100644 profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt index 40f9bdb1..81a23ac0 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt @@ -38,7 +38,9 @@ internal data class HRSServiceData( val data: List = emptyList(), val bodySensorLocation: Int? = null, val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null + val connectionState: GattConnectionState? = null, + val zoomIn: Boolean = false, + val deviceName: String? = null ) { val heartRates = data.map { it.heartRate } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 9f3e8ff5..829e3c9f 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -68,6 +68,14 @@ class HRSRepository @Inject constructor( serviceManager.startService(HRSService::class.java, device) } + fun onInitComplete(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) + } + + fun switchZoomIn() { + _data.value = _data.value.copy(zoomIn = !_data.value.zoomIn) + } + fun onConnectionStateChanged(connectionState: GattConnectionState?) { _data.value = _data.value.copy(connectionState = connectionState) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 137cfc4e..4240f176 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -93,11 +93,11 @@ internal class HRSService : NotificationService() { client.services .filterNotNull() - .onEach { configureGatt(it) } + .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val htsService = services.findService(HRS_SERVICE_UUID)!! val htsMeasurementCharacteristic = htsService.findCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID)!! val bodySensorLocationCharacteristic = htsService.findCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID)!! @@ -116,6 +116,8 @@ internal class HRSService : NotificationService() { .mapNotNull { HRSDataParser.parse(it) } .onEach { repository.onHRSDataChanged(it) } .launchIn(lifecycleScope) + + repository.onInitComplete(device) } private fun stopIfDisconnected(connectionState: GattConnectionState) { diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt index 3b653850..52dbc95c 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt @@ -52,7 +52,7 @@ import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun HRSContentView(state: HRSServiceData, zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) { +internal fun HRSContentView(state: HRSServiceData, onEvent: (HRSScreenViewEvent) -> Unit) { Column( horizontalAlignment = Alignment.CenterHorizontally ) { @@ -61,12 +61,12 @@ internal fun HRSContentView(state: HRSServiceData, zoomIn: Boolean, onEvent: (HR SectionTitle( resId = R.drawable.ic_chart_line, title = stringResource(id = R.string.hrs_section_data), - menu = { Menu(zoomIn, onEvent) } + menu = { Menu(state.zoomIn, onEvent) } ) Spacer(modifier = Modifier.height(16.dp)) - LineChartView(state, zoomIn) + LineChartView(state, state.zoomIn) } Spacer(modifier = Modifier.height(16.dp)) @@ -102,5 +102,5 @@ private fun Menu(zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) { @Preview @Composable private fun Preview() { - HRSContentView(state = HRSServiceData(), zoomIn = false) { } + HRSContentView(state = HRSServiceData()) { } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index c37c2882..3a6b508e 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -47,6 +47,7 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.hrs.R +import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.hrs.viewmodel.HRSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar @@ -70,14 +71,15 @@ fun HRSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.hrsManagerState) { - NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.hrsManagerState.result.connectionState) { + if (state.deviceName == null) { + DeviceConnectingView() + } else { + when (state.connectionState) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> HRSContentView(state.hrsManagerState.result, state.zoomIn) { viewModel.onEvent(it) } + GattConnectionState.STATE_CONNECTED -> HRSContentView(state) { viewModel.onEvent(it) } } } } @@ -85,7 +87,7 @@ fun HRSScreen() { } @Composable -private fun AppBar(state: HRSViewState, navigateUp: () -> Unit, viewModel: HRSViewModel) { +private fun AppBar(state: HRSServiceData, navigateUp: () -> Unit, viewModel: HRSViewModel) { if (state.deviceName?.isNotBlank() == true) { LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt deleted file mode 100644 index d0d3e597..00000000 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt +++ /dev/null @@ -1,46 +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.hrs.view - -import no.nordicsemi.android.hrs.data.HRSServiceData - -internal data class HRSViewState( - val zoomIn: Boolean = false, - val hrsManagerState: HRSManagerState = NoDeviceState, - val deviceName: String? = null -) - -internal sealed class HRSManagerState - -internal data class WorkingState(val result: HRSServiceData) : HRSManagerState() - -internal object NoDeviceState : HRSManagerState() diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index d2237b36..5de6a338 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -50,11 +48,9 @@ import no.nordicsemi.android.hrs.service.HRSRepository import no.nordicsemi.android.hrs.service.HRS_SERVICE_UUID import no.nordicsemi.android.hrs.view.DisconnectEvent import no.nordicsemi.android.hrs.view.HRSScreenViewEvent -import no.nordicsemi.android.hrs.view.HRSViewState import no.nordicsemi.android.hrs.view.NavigateUpEvent import no.nordicsemi.android.hrs.view.OpenLoggerEvent import no.nordicsemi.android.hrs.view.SwitchZoomEvent -import no.nordicsemi.android.hrs.view.WorkingState import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId @@ -67,8 +63,7 @@ internal class HRSViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(HRSViewState()) - val state = _state.asStateFlow() + val state = repository.data init { viewModelScope.launch { @@ -78,8 +73,6 @@ internal class HRSViewModel @Inject constructor( } repository.data.onEach { - _state.value = _state.value.copy(hrsManagerState = WorkingState(it)) - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.HRS)) } @@ -102,7 +95,6 @@ internal class HRSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _state.value = _state.value.copy(deviceName = device.name) repository.launch(device) } @@ -116,7 +108,7 @@ internal class HRSViewModel @Inject constructor( } private fun onZoomButtonClicked() { - _state.value = _state.value.copy(zoomIn = !_state.value.zoomIn) + repository.switchZoomIn() } private fun disconnect() { From 6e1a23341beab05a16f340fc217647d7f5cbabd8 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 20 Mar 2023 13:54:49 +0100 Subject: [PATCH 019/101] Fix HTS profile --- .../android/hts/data/HTSServiceData.kt | 5 +- .../android/hts/repository/HTSRepository.kt | 9 ++++ .../android/hts/repository/HTSService.kt | 6 ++- .../android/hts/view/HTSContentView.kt | 8 ++-- .../nordicsemi/android/hts/view/HTSScreen.kt | 12 +++-- .../nordicsemi/android/hts/view/HTSState.kt | 46 ------------------- .../android/hts/viewmodel/HTSViewModel.kt | 12 +---- 7 files changed, 30 insertions(+), 68 deletions(-) delete mode 100644 profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt index e5273518..210c4b8c 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt @@ -31,11 +31,14 @@ package no.nordicsemi.android.hts.data +import no.nordicsemi.android.hts.view.TemperatureUnit import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData internal data class HTSServiceData( val data: HTSData = HTSData(), val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null + val connectionState: GattConnectionState? = null, + val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS, + val deviceName: String? = null ) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 01de2357..a0e921de 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.hts.data.HTSServiceData +import no.nordicsemi.android.hts.view.TemperatureUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData @@ -68,6 +69,14 @@ class HTSRepository @Inject constructor( serviceManager.startService(HTSService::class.java, device) } + fun onInitComplete(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) + } + + internal fun setTemperatureUnit(temperatureUnit: TemperatureUnit) { + _data.value = _data.value.copy(temperatureUnit = temperatureUnit) + } + fun onConnectionStateChanged(connectionState: GattConnectionState?) { _data.value = _data.value.copy(connectionState = connectionState) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index f6bd271b..7094c524 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -91,11 +91,11 @@ internal class HTSService : NotificationService() { client.services .filterNotNull() - .onEach { configureGatt(it) } + .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val htsService = services.findService(HTS_SERVICE_UUID)!! val htsMeasurementCharacteristic = htsService.findCharacteristic(HTS_MEASUREMENT_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! @@ -110,6 +110,8 @@ internal class HTSService : NotificationService() { .mapNotNull { HTSDataParser.parse(it) } .onEach { repository.onHTSDataChanged(it) } .launchIn(lifecycleScope) + + repository.onInitComplete(device) } private fun stopIfDisconnected(connectionState: GattConnectionState) { diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt index d46a4a0f..4851bbc5 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt @@ -52,7 +52,7 @@ import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun HTSContentView(state: HTSServiceData, temperatureUnit: TemperatureUnit, onEvent: (HTSScreenViewEvent) -> Unit) { +internal fun HTSContentView(state: HTSServiceData, onEvent: (HTSScreenViewEvent) -> Unit) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally @@ -62,7 +62,7 @@ internal fun HTSContentView(state: HTSServiceData, temperatureUnit: TemperatureU Spacer(modifier = Modifier.height(16.dp)) - RadioButtonGroup(viewEntity = temperatureUnit.temperatureSettingsItems()) { + RadioButtonGroup(viewEntity = state.temperatureUnit.temperatureSettingsItems()) { onEvent(OnTemperatureUnitSelected(it.label.toTemperatureUnit())) } } @@ -76,7 +76,7 @@ internal fun HTSContentView(state: HTSServiceData, temperatureUnit: TemperatureU KeyValueField( stringResource(id = R.string.hts_temperature), - displayTemperature(state.data.temperature, temperatureUnit) + displayTemperature(state.data.temperature, state.temperatureUnit) ) } @@ -99,5 +99,5 @@ internal fun HTSContentView(state: HTSServiceData, temperatureUnit: TemperatureU @Preview @Composable private fun Preview() { - HTSContentView(state = HTSServiceData(), TemperatureUnit.CELSIUS) { } + HTSContentView(state = HTSServiceData()) { } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 9a406c24..1669179b 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -47,6 +47,7 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.hts.R +import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.hts.viewmodel.HTSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar @@ -70,14 +71,15 @@ fun HTSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.htsManagerState) { - NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.htsManagerState.result.connectionState) { + if (state.deviceName == null) { + DeviceConnectingView() + } else { + when (state.connectionState) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> HTSContentView(state.htsManagerState.result, state.temperatureUnit) { viewModel.onEvent(it) } + GattConnectionState.STATE_CONNECTED -> HTSContentView(state) { viewModel.onEvent(it) } } } } @@ -85,7 +87,7 @@ fun HTSScreen() { } @Composable -private fun AppBar(state: HTSViewState, navigateUp: () -> Unit, viewModel: HTSViewModel) { +private fun AppBar(state: HTSServiceData, navigateUp: () -> Unit, viewModel: HTSViewModel) { if (state.deviceName?.isNotBlank() == true) { LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt deleted file mode 100644 index ef122680..00000000 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSState.kt +++ /dev/null @@ -1,46 +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.hts.view - -import no.nordicsemi.android.hts.data.HTSServiceData - -internal data class HTSViewState( - val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS, - val htsManagerState: HTSManagerState = NoDeviceState, - val deviceName: String? = null -) - -internal sealed class HTSManagerState - -internal data class WorkingState(val result: HTSServiceData) : HTSManagerState() - -internal object NoDeviceState : HTSManagerState() diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index c5a77335..e9d516ad 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -50,11 +48,9 @@ import no.nordicsemi.android.hts.repository.HTSRepository import no.nordicsemi.android.hts.repository.HTS_SERVICE_UUID import no.nordicsemi.android.hts.view.DisconnectEvent import no.nordicsemi.android.hts.view.HTSScreenViewEvent -import no.nordicsemi.android.hts.view.HTSViewState import no.nordicsemi.android.hts.view.NavigateUp import no.nordicsemi.android.hts.view.OnTemperatureUnitSelected import no.nordicsemi.android.hts.view.OpenLoggerEvent -import no.nordicsemi.android.hts.view.WorkingState import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId @@ -67,8 +63,7 @@ internal class HTSViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(HTSViewState()) - val state = _state.asStateFlow() + val state = repository.data init { viewModelScope.launch { @@ -78,8 +73,6 @@ internal class HTSViewModel @Inject constructor( } repository.data.onEach { - _state.value = _state.value.copy(htsManagerState = WorkingState(it)) - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.HTS)) } @@ -102,7 +95,6 @@ internal class HTSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _state.value = _state.value.copy(deviceName = device.name) repository.launch(device) } @@ -121,6 +113,6 @@ internal class HTSViewModel @Inject constructor( } private fun onTemperatureUnitSelected(event: OnTemperatureUnitSelected) { - _state.value = _state.value.copy(temperatureUnit = event.value) + repository.setTemperatureUnit(event.value) } } From 702841fd8d3fa82b85c32e3c5e7e903379093e59 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 20 Mar 2023 13:57:42 +0100 Subject: [PATCH 020/101] Fix RSCS profile --- .../android/rscs/data/RSCSServiceData.kt | 3 +- .../android/rscs/repository/RSCSRepository.kt | 4 ++ .../android/rscs/repository/RSCSService.kt | 6 ++- .../android/rscs/view/RSCSScreen.kt | 12 ++--- .../nordicsemi/android/rscs/view/RSCSState.kt | 45 ------------------- .../android/rscs/viewmodel/RSCSViewModel.kt | 10 +---- 6 files changed, 18 insertions(+), 62 deletions(-) delete mode 100644 profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt index 1e01e679..f98884f8 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt @@ -40,7 +40,8 @@ import no.nordicsemi.android.rscs.R internal data class RSCSServiceData( val data: RSCSData = RSCSData(), val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null + val connectionState: GattConnectionState? = null, + val deviceName: String? = null ) { @Composable fun displayActivity(): String { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index a96cef9c..36b935fd 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -68,6 +68,10 @@ class RSCSRepository @Inject constructor( serviceManager.startService(RSCSService::class.java, device) } + fun onInitComplete(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) + } + fun onConnectionStateChanged(connectionState: GattConnectionState?) { _data.value = _data.value.copy(connectionState = connectionState) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index 7c907373..e29b9b1e 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -91,11 +91,11 @@ internal class RSCSService : NotificationService() { client.services .filterNotNull() - .onEach { configureGatt(it) } + .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val rscsService = services.findService(RSCS_SERVICE_UUID)!! val rscsMeasurementCharacteristic = rscsService.findCharacteristic(RSC_MEASUREMENT_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! @@ -110,6 +110,8 @@ internal class RSCSService : NotificationService() { .mapNotNull { RSCSDataParser.parse(it) } .onEach { repository.onRSCSDataChanged(it) } .launchIn(lifecycleScope) + + repository.onInitComplete(device) } private fun stopIfDisconnected(connectionState: GattConnectionState) { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index 21beeced..168233c1 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -48,6 +48,7 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.rscs.R +import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.rscs.viewmodel.RSCSViewModel import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar @@ -70,14 +71,15 @@ fun RSCSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.rscsManagerState) { - NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.rscsManagerState.result.connectionState) { + if (state.deviceName == null) { + DeviceConnectingView() + } else { + when (state.connectionState) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> RSCSContentView(state.rscsManagerState.result) { viewModel.onEvent(it) } + GattConnectionState.STATE_CONNECTED -> RSCSContentView(state) { viewModel.onEvent(it) } } } } @@ -85,7 +87,7 @@ fun RSCSScreen() { } @Composable -private fun AppBar(state: RSCSViewState, navigateUp: () -> Unit, viewModel: RSCSViewModel) { +private fun AppBar(state: RSCSServiceData, navigateUp: () -> Unit, viewModel: RSCSViewModel) { if (state.deviceName?.isNotBlank() == true) { LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt deleted file mode 100644 index 56710f1f..00000000 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt +++ /dev/null @@ -1,45 +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.rscs.view - -import no.nordicsemi.android.rscs.data.RSCSServiceData - -internal data class RSCSViewState( - val rscsManagerState: RSCSManagerState = NoDeviceState, - val deviceName: String? = null -) - -internal sealed class RSCSManagerState - -internal data class WorkingState(val result: RSCSServiceData) : RSCSManagerState() - -internal object NoDeviceState : RSCSManagerState() diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 3ac505e8..50d6d80a 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -53,9 +51,7 @@ import no.nordicsemi.android.rscs.repository.RSCS_SERVICE_UUID import no.nordicsemi.android.rscs.view.DisconnectEvent import no.nordicsemi.android.rscs.view.NavigateUpEvent import no.nordicsemi.android.rscs.view.OpenLoggerEvent -import no.nordicsemi.android.rscs.view.RSCSViewState import no.nordicsemi.android.rscs.view.RSCScreenViewEvent -import no.nordicsemi.android.rscs.view.WorkingState import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -66,8 +62,7 @@ internal class RSCSViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(RSCSViewState()) - val state = _state.asStateFlow() + val state = repository.data init { viewModelScope.launch { @@ -77,8 +72,6 @@ internal class RSCSViewModel @Inject constructor( } repository.data.onEach { - _state.value = _state.value.copy(rscsManagerState = WorkingState(it)) - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.RSCS)) } @@ -101,7 +94,6 @@ internal class RSCSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _state.value = _state.value.copy(deviceName = device.name) repository.launch(device) } From 53274043491de4ba243ed927d156adf34e0ba283 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 20 Mar 2023 15:53:29 +0100 Subject: [PATCH 021/101] WIP: Migrate PRX to the new BLE library --- profile_prx/build.gradle.kts | 1 + .../no/nordicsemi/android/prx/data/PRXData.kt | 52 --------- .../nordicsemi/android/prx/data/PRXManager.kt | 1 + .../android/prx/data/PRXServiceData.kt | 13 +++ .../android/prx/repository/PRXRepository.kt | 56 ++++----- .../android/prx/repository/PRXService.kt | 108 +++++++++++++++++- .../android/prx/view/PRXContentView.kt | 1 - .../nordicsemi/android/prx/view/PRXState.kt | 41 ------- .../android/prx/viewmodel/PRXViewModel.kt | 14 +-- 9 files changed, 145 insertions(+), 142 deletions(-) delete mode 100644 profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXData.kt create mode 100644 profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt delete mode 100644 profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXState.kt diff --git a/profile_prx/build.gradle.kts b/profile_prx/build.gradle.kts index ade0bcee..58cea4fe 100644 --- a/profile_prx/build.gradle.kts +++ b/profile_prx/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(libs.nordic.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.nordic.core) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXData.kt deleted file mode 100644 index 1488297d..00000000 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXData.kt +++ /dev/null @@ -1,52 +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.prx.data - -internal data class PRXData( - val batteryLevel: Int? = null, - val localAlarmLevel: AlarmLevel = AlarmLevel.NONE, - val isRemoteAlarm: Boolean = false, - val linkLossAlarmLevel: AlarmLevel = AlarmLevel.HIGH -) - -internal enum class AlarmLevel(val value: Int) { - NONE(0x00), - MEDIUM(0x01), - HIGH(0x02); - - companion object { - fun create(value: Int): AlarmLevel { - return AlarmLevel.values().firstOrNull { it.value == value } - ?: throw IllegalArgumentException("Cannot find AlarmLevel for provided value: $value") - } - } -} diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXManager.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXManager.kt index 292b2bf8..d5a11630 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXManager.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXManager.kt @@ -47,6 +47,7 @@ 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.common.logger.NordicLogger +import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import no.nordicsemi.android.service.ConnectionObserverAdapter import no.nordicsemi.android.utils.launchWithCatch import java.util.* diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt new file mode 100644 index 00000000..2c520815 --- /dev/null +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -0,0 +1,13 @@ +package no.nordicsemi.android.prx.data + +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel + +data class PRXServiceData( + val localAlarmLevel: AlarmLevel = AlarmLevel.NONE, + val linkLossAlarmLevel: AlarmLevel = AlarmLevel.HIGH, + val batteryLevel: Int? = null, + val connectionState: GattConnectionState? = null, + val isRemoteAlarm: Boolean = false, + val deviceName: String? = null +) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 20b4c13e..c420435a 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -33,21 +33,22 @@ package no.nordicsemi.android.prx.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach +import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.prx.data.AlarmLevel -import no.nordicsemi.android.prx.data.PRXData -import no.nordicsemi.android.prx.data.PRXManager +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData +import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel +import no.nordicsemi.android.kotlin.ble.profile.prx.PRXData +import no.nordicsemi.android.prx.data.PRXServiceData import no.nordicsemi.android.prx.data.ProximityServerManager import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.service.IdleResult +import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.LinkLossResult import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.service.SuccessResult @@ -66,37 +67,24 @@ class PRXRepository @Inject internal constructor( private val stringConst: StringConst ) { - private var manager: PRXManager? = null - private var logger: NordicLogger? = null - - private val _data = MutableStateFlow>(IdleResult()) + private val _data = MutableStateFlow(PRXServiceData()) internal val data = _data.asStateFlow() - val isRunning = data.map { it.isRunning() } - val hasBeenDisconnectedWithoutLinkLoss = data.map { it.hasBeenDisconnectedWithoutLinkLoss() } + private val _stopEvent = simpleSharedFlow() + internal val stopEvent = _stopEvent.asSharedFlow() + + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(PRXService::class.java, device) - proximityServerManager.open() } - fun start(device: ServerDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "PRX", device.address).also { - logger = it - } - val manager = PRXManager(context, scope, createdLogger) - this.manager = manager - manager.useServer(proximityServerManager) + fun onInitComplete(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) + } - manager.dataHolder.status.onEach { - _data.value = it - handleLocalAlarm(it) - }.launchIn(scope) - -// manager.connect(device.device) -// .useAutoConnect(true) -// .retry(3, 100) -// .enqueue() + fun onConnectionStateChanged(connectionState: GattConnectionState?) { + _data.value = _data.value.copy(connectionState = connectionState) } private fun handleLocalAlarm(result: BleManagerResult) { @@ -113,6 +101,10 @@ class PRXRepository @Inject internal constructor( } } + fun onBatteryLevelChanged(batteryLevel: Int) { + _data.value = _data.value.copy(batteryLevel = batteryLevel) + } + fun enableAlarm() { manager?.writeImmediateAlert(true) } @@ -127,8 +119,6 @@ class PRXRepository @Inject internal constructor( fun release() { disableAlarm() - manager?.disconnect()?.enqueue() - manager = null - logger = null + _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 9925bf4b..7dbf20b7 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -31,33 +31,133 @@ package no.nordicsemi.android.prx.repository +import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission +import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.server.BleGattServer +import no.nordicsemi.android.kotlin.ble.core.server.service.service.BleGattServerServiceType +import no.nordicsemi.android.kotlin.ble.core.server.service.service.BleServerGattCharacteristicConfig +import no.nordicsemi.android.kotlin.ble.core.server.service.service.BleServerGattServiceConfig +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.hts.HTSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import java.util.* import javax.inject.Inject +val PRX_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb") +private val LINK_LOSS_SERVICE_UUID = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb") +private 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") + +@SuppressLint("MissingPermission") @AndroidEntryPoint internal class PRXService : NotificationService() { @Inject lateinit var repository: PRXRepository + private lateinit var client: BleGattClient + + private lateinit var alertLevelCharacteristic: BleGattCharacteristic + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) val device = intent!!.getParcelableExtra(DEVICE_DATA)!! - repository.start(device, lifecycleScope) + startGattClient(device) - repository.hasBeenDisconnectedWithoutLinkLoss.onEach { - if (it) stopSelf() - }.launchIn(lifecycleScope) + repository.stopEvent + .onEach { disconnect() } + .launchIn(lifecycleScope) return START_REDELIVER_INTENT } + + private fun startServer() { + val alertLevelCharacteristic = BleServerGattCharacteristicConfig( + ALERT_LEVEL_CHARACTERISTIC_UUID, + listOf(BleGattProperty.PROPERTY_WRITE_NO_RESPONSE), + listOf(BleGattPermission.PERMISSION_WRITE) + ) + + val linkLossCharacteristic = BleServerGattCharacteristicConfig( + LINK_LOSS_SERVICE_UUID, + listOf(BleGattProperty.PROPERTY_WRITE, BleGattProperty.PROPERTY_READ), + listOf(BleGattPermission.PERMISSION_WRITE, BleGattPermission.PERMISSION_READ) + ) + + val serviceConfig = BleServerGattServiceConfig( + PRX_SERVICE_UUID, + BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + listOf(alertLevelCharacteristic, linkLossCharacteristic) + ) + + val server = BleGattServer.create(this@PRXService, serviceConfig) + + TODO("Initialize characteristic with value") + } + + private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { + client = device.connect(this@PRXService) + + client.connectionState + .onEach { repository.onConnectionStateChanged(it) } + .filterNotNull() + .onEach { stopIfDisconnected(it) } + .launchIn(lifecycleScope) + + client.services + .filterNotNull() + .onEach { configureGatt(it, device) } + .launchIn(lifecycleScope) + } + + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + val prxService = services.findService(PRX_SERVICE_UUID)!! + alertLevelCharacteristic = prxService.findCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)!! + val linkLossCharacteristic = prxService.findCharacteristic(LINK_LOSS_SERVICE_UUID)!! + val batteryService = services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + batteryLevelCharacteristic.getNotifications() + .mapNotNull { BatteryLevelParser.parse(it) } + .onEach { repository.onBatteryLevelChanged(it) } + .launchIn(lifecycleScope) + + linkLossCharacteristic.write(Alert) + + htsMeasurementCharacteristic.getNotifications() + .mapNotNull { HTSDataParser.parse(it) } + .onEach { repository.onHTSDataChanged(it) } + .launchIn(lifecycleScope) + + repository.onInitComplete(device) + } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + stopSelf() + } + } + + private fun disconnect() { + client.disconnect() + } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt index e47fa8b1..16fd4a71 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt @@ -44,7 +44,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import no.nordicsemi.android.prx.R -import no.nordicsemi.android.prx.data.PRXData import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXState.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXState.kt deleted file mode 100644 index c95e4004..00000000 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXState.kt +++ /dev/null @@ -1,41 +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.prx.view - -import no.nordicsemi.android.prx.data.PRXData -import no.nordicsemi.android.service.BleManagerResult - -internal sealed class PRXViewState - -internal data class WorkingState(val result: BleManagerResult) : PRXViewState() - -internal object NoDeviceState : PRXViewState() diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index 6acfe572..093be6e7 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -47,18 +45,15 @@ import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.prx.data.PRX_SERVICE_UUID import no.nordicsemi.android.prx.repository.PRXRepository import no.nordicsemi.android.prx.view.DisconnectEvent import no.nordicsemi.android.prx.view.NavigateUpEvent -import no.nordicsemi.android.prx.view.NoDeviceState import no.nordicsemi.android.prx.view.OpenLoggerEvent import no.nordicsemi.android.prx.view.PRXScreenViewEvent -import no.nordicsemi.android.prx.view.PRXViewState import no.nordicsemi.android.prx.view.TurnOffAlert import no.nordicsemi.android.prx.view.TurnOnAlert -import no.nordicsemi.android.prx.view.WorkingState -import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -69,8 +64,7 @@ internal class PRXViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(NoDeviceState) - val state = _state.asStateFlow() + val state = repository.data init { viewModelScope.launch { @@ -80,9 +74,7 @@ internal class PRXViewModel @Inject constructor( } repository.data.onEach { - _state.value = WorkingState(it) - - (it as? ConnectedResult)?.let { + if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.PRX)) } }.launchIn(viewModelScope) From a5306eaf71d88076b2cf4c1f2336dbc58340b77a Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 21 Mar 2023 10:40:31 +0100 Subject: [PATCH 022/101] Migrate PRX module to the new BLE library --- .../nordicsemi/android/prx/data/PRXManager.kt | 179 ------------------ .../prx/data/ProximityServerManager.kt | 76 -------- .../android/prx/repository/AlarmHandler.kt | 2 +- .../android/prx/repository/PRXRepository.kt | 28 +-- .../android/prx/repository/PRXService.kt | 81 ++++++-- .../android/prx/view/PRXContentView.kt | 7 +- .../nordicsemi/android/prx/view/PRXMapper.kt | 2 +- .../nordicsemi/android/prx/view/PRXScreen.kt | 45 ++--- .../android/prx/viewmodel/PRXViewModel.kt | 7 +- 9 files changed, 104 insertions(+), 323 deletions(-) delete mode 100644 profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXManager.kt delete mode 100644 profile_prx/src/main/java/no/nordicsemi/android/prx/data/ProximityServerManager.kt diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXManager.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXManager.kt deleted file mode 100644 index d5a11630..00000000 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXManager.kt +++ /dev/null @@ -1,179 +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.prx.data - -import android.bluetooth.BluetoothDevice -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic -import android.bluetooth.BluetoothGattServer -import android.content.Context -import android.util.Log -import kotlinx.coroutines.CoroutineScope -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.common.logger.NordicLogger -import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel -import no.nordicsemi.android.service.ConnectionObserverAdapter -import no.nordicsemi.android.utils.launchWithCatch -import java.util.* - -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, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : 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 var isAlertEnabled = false - - private val data = MutableStateFlow(PRXData()) - val dataHolder = ConnectionObserverAdapter() - - init { - connectionObserver = dataHolder - - data.onEach { - dataHolder.setValue(it) - }.launchIn(scope) - } - - override fun log(priority: Int, message: String) { - logger.log(priority, message) - } - - override fun getMinLogPriority(): Int { - return Log.VERBOSE - } - - private inner class ProximityManagerGattCallback : BleManagerGattCallback() { - override fun initialize() { - super.initialize() - - setWriteCallback(localAlertLevelCharacteristic) - .with(object : AlertLevelDataCallback() { - override fun onAlertLevelChanged(device: BluetoothDevice, level: Int) { - data.value = data.value.copy(localAlarmLevel = AlarmLevel.create(level)) - } - }) - - setWriteCallback(linkLossServerCharacteristic) - .with(object : AlertLevelDataCallback() { - override fun onAlertLevelChanged(device: BluetoothDevice, level: Int) { - data.value = data.value.copy(linkLossAlarmLevel = AlarmLevel.create(level)) - } - }) - - writeCharacteristic( - linkLossCharacteristic, - AlertLevelData.highAlert(), - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - ).enqueue() - - setNotificationCallback(batteryLevelCharacteristic) - .asValidResponseFlow() - .onEach { - data.value = data.value.copy(batteryLevel = it.batteryLevel) - }.launchIn(scope) - enableNotifications(batteryLevelCharacteristic).enqueue() - } - - override fun onServerReady(server: BluetoothGattServer) { - val immediateAlertService = server.getService(PRX_SERVICE_UUID) - if (immediateAlertService != null) { - localAlertLevelCharacteristic = immediateAlertService.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID) - } - val linkLossService = server.getService(LINK_LOSS_SERVICE_UUID) - if (linkLossService != null) { - linkLossServerCharacteristic = linkLossService.getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID) - } - } - - override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - 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) - } - gatt.getService(PRX_SERVICE_UUID)?.run { - alertLevelCharacteristic = getCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID) - } - return linkLossCharacteristic != null - } - - override fun onServicesInvalidated() { - batteryLevelCharacteristic = null - alertLevelCharacteristic = null - linkLossCharacteristic = null - localAlertLevelCharacteristic = null - linkLossServerCharacteristic = null - isAlertEnabled = false - } - } - - fun writeImmediateAlert(on: Boolean) { - if (!isConnected) return - scope.launchWithCatch { - writeCharacteristic( - alertLevelCharacteristic, - if (on) AlertLevelData.highAlert() else AlertLevelData.noAlert(), - BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE - ).suspend() - - isAlertEnabled = on - data.value = data.value.copy(isRemoteAlarm = on) - } - } - - override fun getGattCallback(): BleManagerGattCallback { - return ProximityManagerGattCallback() - } -} diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/ProximityServerManager.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/ProximityServerManager.kt deleted file mode 100644 index b355086b..00000000 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/ProximityServerManager.kt +++ /dev/null @@ -1,76 +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.prx.data - -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 javax.inject.Inject - -internal class ProximityServerManager @Inject constructor( - @ApplicationContext - context: Context -) : BleServerManager(context) { - - override fun log(priority: Int, message: String) { - Log.println(priority, "BleManager", message) - } - - override fun initializeServer(): List { - val services: MutableList = ArrayList() - services.add( - service( - PRX_SERVICE_UUID, - characteristic( - ALERT_LEVEL_CHARACTERISTIC_UUID, - BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, - BluetoothGattCharacteristic.PERMISSION_WRITE - ) - ) - ) - services.add( - service( - LINK_LOSS_SERVICE_UUID, - characteristic( - ALERT_LEVEL_CHARACTERISTIC_UUID, - BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_READ, - BluetoothGattCharacteristic.PERMISSION_WRITE or BluetoothGattCharacteristic.PERMISSION_READ, - AlertLevelData.highAlert() - ) - ) - ) - return services - } -} diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt index 2d779909..eccef2ee 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt @@ -35,7 +35,7 @@ import android.content.Context import android.media.RingtoneManager import android.os.Build import dagger.hilt.android.qualifiers.ApplicationContext -import no.nordicsemi.android.prx.data.AlarmLevel +import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import javax.inject.Inject internal class AlarmHandler @Inject constructor( diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index c420435a..58acccbb 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -42,11 +42,9 @@ import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import no.nordicsemi.android.kotlin.ble.profile.prx.PRXData import no.nordicsemi.android.prx.data.PRXServiceData -import no.nordicsemi.android.prx.data.ProximityServerManager import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.LinkLossResult @@ -61,10 +59,7 @@ class PRXRepository @Inject internal constructor( @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val proximityServerManager: ProximityServerManager, private val alarmHandler: AlarmHandler, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst ) { private val _data = MutableStateFlow(PRXServiceData()) @@ -73,6 +68,9 @@ class PRXRepository @Inject internal constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() + private val _remoteAlarmLevel = simpleSharedFlow() + internal val remoteAlarmLevel = _remoteAlarmLevel.asSharedFlow() + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { @@ -87,6 +85,14 @@ class PRXRepository @Inject internal constructor( _data.value = _data.value.copy(connectionState = connectionState) } + fun setLocalAlarmLevel(alarmLevel: AlarmLevel) { + _data.value = _data.value.copy(localAlarmLevel = alarmLevel) + } + + fun setLinkLossAlarmLevel(alarmLevel: AlarmLevel) { + _data.value = _data.value.copy(linkLossAlarmLevel = alarmLevel) + } + private fun handleLocalAlarm(result: BleManagerResult) { (result as? SuccessResult)?.let { if (it.data.localAlarmLevel != AlarmLevel.NONE) { @@ -105,20 +111,20 @@ class PRXRepository @Inject internal constructor( _data.value = _data.value.copy(batteryLevel = batteryLevel) } - fun enableAlarm() { - manager?.writeImmediateAlert(true) + fun setRemoteAlarmLevel(alarmLevel: AlarmLevel) { + _remoteAlarmLevel.tryEmit(alarmLevel) } - fun disableAlarm() { - manager?.writeImmediateAlert(false) + fun onRemoteAlarmLevelSet(alarmLevel: AlarmLevel) { + _data.value = _data.value.copy(isRemoteAlarm = alarmLevel != AlarmLevel.NONE) } fun openLogger() { - NordicLogger.launch(context, logger) + TODO() } fun release() { - disableAlarm() + _remoteAlarmLevel.tryEmit(AlarmLevel.NONE) _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 7dbf20b7..707b0207 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -33,6 +33,7 @@ package no.nordicsemi.android.prx.repository import android.annotation.SuppressLint import android.content.Intent +import android.util.Log import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.filterNotNull @@ -41,6 +42,7 @@ import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.BleWriteType import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristic import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices @@ -51,8 +53,11 @@ import no.nordicsemi.android.kotlin.ble.core.server.BleGattServer import no.nordicsemi.android.kotlin.ble.core.server.service.service.BleGattServerServiceType import no.nordicsemi.android.kotlin.ble.core.server.service.service.BleServerGattCharacteristicConfig import no.nordicsemi.android.kotlin.ble.core.server.service.service.BleServerGattServiceConfig +import no.nordicsemi.android.kotlin.ble.core.server.service.service.BluetoothGattServerConnection import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser -import no.nordicsemi.android.kotlin.ble.profile.hts.HTSDataParser +import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel +import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevelParser +import no.nordicsemi.android.kotlin.ble.profile.prx.AlertLevelInputParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import java.util.* @@ -60,6 +65,7 @@ import javax.inject.Inject val PRX_SERVICE_UUID = UUID.fromString("00001802-0000-1000-8000-00805f9b34fb") private val LINK_LOSS_SERVICE_UUID = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb") + private val ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A06-0000-1000-8000-00805f9b34fb") private val BATTERY_SERVICE_UUID = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb") @@ -73,6 +79,7 @@ internal class PRXService : NotificationService() { lateinit var repository: PRXRepository private lateinit var client: BleGattClient + private lateinit var server: BleGattServer private lateinit var alertLevelCharacteristic: BleGattCharacteristic @@ -81,37 +88,69 @@ internal class PRXService : NotificationService() { val device = intent!!.getParcelableExtra(DEVICE_DATA)!! + startServer() + startGattClient(device) repository.stopEvent .onEach { disconnect() } .launchIn(lifecycleScope) + repository.remoteAlarmLevel + .onEach { writeAlertLevel(it) } + .launchIn(lifecycleScope) + return START_REDELIVER_INTENT } private fun startServer() { val alertLevelCharacteristic = BleServerGattCharacteristicConfig( - ALERT_LEVEL_CHARACTERISTIC_UUID, - listOf(BleGattProperty.PROPERTY_WRITE_NO_RESPONSE), - listOf(BleGattPermission.PERMISSION_WRITE) + uuid = ALERT_LEVEL_CHARACTERISTIC_UUID, + properties = listOf(BleGattProperty.PROPERTY_WRITE_NO_RESPONSE), + permissions = listOf(BleGattPermission.PERMISSION_WRITE) + ) + val prxServiceConfig = BleServerGattServiceConfig( + uuid = PRX_SERVICE_UUID, + type = BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + characteristicConfigs = listOf(alertLevelCharacteristic) ) val linkLossCharacteristic = BleServerGattCharacteristicConfig( - LINK_LOSS_SERVICE_UUID, - listOf(BleGattProperty.PROPERTY_WRITE, BleGattProperty.PROPERTY_READ), - listOf(BleGattPermission.PERMISSION_WRITE, BleGattPermission.PERMISSION_READ) + uuid = ALERT_LEVEL_CHARACTERISTIC_UUID, + properties = listOf(BleGattProperty.PROPERTY_WRITE, BleGattProperty.PROPERTY_READ), + permissions = listOf(BleGattPermission.PERMISSION_WRITE, BleGattPermission.PERMISSION_READ), + initialValue = AlertLevelInputParser.parse(AlarmLevel.HIGH) ) - val serviceConfig = BleServerGattServiceConfig( - PRX_SERVICE_UUID, - BleGattServerServiceType.SERVICE_TYPE_PRIMARY, - listOf(alertLevelCharacteristic, linkLossCharacteristic) + val linkLossServiceConfig = BleServerGattServiceConfig( + uuid = LINK_LOSS_SERVICE_UUID, + type = BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + characteristicConfigs = listOf(linkLossCharacteristic) ) - val server = BleGattServer.create(this@PRXService, serviceConfig) + server = BleGattServer.create(this@PRXService, prxServiceConfig, linkLossServiceConfig) - TODO("Initialize characteristic with value") + server.onNewConnection + .onEach { setUpServerConnection(it.second) } + .launchIn(lifecycleScope) + } + + private fun setUpServerConnection(connection: BluetoothGattServerConnection) { + val prxService = connection.services.findService(PRX_SERVICE_UUID)!! + val linkLossService = connection.services.findService(LINK_LOSS_SERVICE_UUID)!! + + val prxCharacteristic = prxService.findCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)!! + val linkLossCharacteristic = linkLossService.findCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)!! + + prxCharacteristic.value + .mapNotNull { AlarmLevelParser.parse(it) } + .onEach { repository.setLocalAlarmLevel(it) } + .launchIn(lifecycleScope) + + linkLossCharacteristic.value + .mapNotNull { AlarmLevelParser.parse(it) } + .onEach { repository.setLocalAlarmLevel(it) } + .launchIn(lifecycleScope) } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { @@ -132,7 +171,8 @@ internal class PRXService : NotificationService() { private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val prxService = services.findService(PRX_SERVICE_UUID)!! alertLevelCharacteristic = prxService.findCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)!! - val linkLossCharacteristic = prxService.findCharacteristic(LINK_LOSS_SERVICE_UUID)!! + val linkLossService = services.findService(LINK_LOSS_SERVICE_UUID)!! + val linkLossCharacteristic = linkLossService.findCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! @@ -141,16 +181,16 @@ internal class PRXService : NotificationService() { .onEach { repository.onBatteryLevelChanged(it) } .launchIn(lifecycleScope) - linkLossCharacteristic.write(Alert) - - htsMeasurementCharacteristic.getNotifications() - .mapNotNull { HTSDataParser.parse(it) } - .onEach { repository.onHTSDataChanged(it) } - .launchIn(lifecycleScope) + linkLossCharacteristic.write(AlertLevelInputParser.parse(AlarmLevel.HIGH)) repository.onInitComplete(device) } + private suspend fun writeAlertLevel(alarmLevel: AlarmLevel) { + alertLevelCharacteristic.write(AlertLevelInputParser.parse(alarmLevel), BleWriteType.NO_RESPONSE) + repository.onRemoteAlarmLevelSet(alarmLevel) + } + private fun stopIfDisconnected(connectionState: GattConnectionState) { if (connectionState == GattConnectionState.STATE_DISCONNECTED) { stopSelf() @@ -159,5 +199,6 @@ internal class PRXService : NotificationService() { private fun disconnect() { client.disconnect() + server.stopServer() } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt index 16fd4a71..1f2a13f8 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXContentView.kt @@ -44,13 +44,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import no.nordicsemi.android.prx.R +import no.nordicsemi.android.prx.data.PRXServiceData import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun ContentView(state: PRXData, onEvent: (PRXScreenViewEvent) -> Unit) { +internal fun ContentView(state: PRXServiceData, onEvent: (PRXScreenViewEvent) -> Unit) { Column( horizontalAlignment = Alignment.CenterHorizontally ) { @@ -77,7 +78,7 @@ internal fun ContentView(state: PRXData, onEvent: (PRXScreenViewEvent) -> Unit) } @Composable -private fun SettingsSection(state: PRXData, onEvent: (PRXScreenViewEvent) -> Unit) { +private fun SettingsSection(state: PRXServiceData, onEvent: (PRXScreenViewEvent) -> Unit) { ScreenSection { SectionTitle(icon = Icons.Default.Settings, title = stringResource(R.string.prx_settings)) @@ -110,7 +111,7 @@ private fun TurnAlarmOffButton(onEvent: (PRXScreenViewEvent) -> Unit) { } @Composable -private fun RecordsSection(state: PRXData) { +private fun RecordsSection(state: PRXServiceData) { ScreenSection { SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.prx_records)) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXMapper.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXMapper.kt index f687a018..4f9bf5d2 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXMapper.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXMapper.kt @@ -33,8 +33,8 @@ package no.nordicsemi.android.prx.view import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource +import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import no.nordicsemi.android.prx.R -import no.nordicsemi.android.prx.data.AlarmLevel @Composable internal fun Boolean.toDisplayString(): String { diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index b20d2f3c..0a453206 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -46,17 +46,10 @@ import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.prx.R +import no.nordicsemi.android.prx.data.PRXServiceData import no.nordicsemi.android.prx.viewmodel.PRXViewModel -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult -import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -78,17 +71,15 @@ fun PRXScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state) { - NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.result) { - is IdleResult, - is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) } - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) } - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - is SuccessResult -> ContentView(state.result.data) { viewModel.onEvent(it) } + if (state.deviceName == null) { + DeviceConnectingView() + } else { + when (state.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> ContentView(state) { viewModel.onEvent(it) } } } } @@ -96,16 +87,12 @@ fun PRXScreen() { } @Composable -private fun AppBar(state: PRXViewState, navigateUp: () -> Unit, viewModel: PRXViewModel) { - val toolbarName = (state as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { - BackIconAppBar(stringResource(id = R.string.prx_title), navigateUp) - } else { - LoggerIconAppBar(toolbarName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { +private fun AppBar(state: PRXServiceData, navigateUp: () -> Unit, viewModel: PRXViewModel) { + if (state.deviceName?.isNotBlank() == true) { + LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) } + } else { + BackIconAppBar(stringResource(id = R.string.prx_title), navigateUp) } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index 093be6e7..080a86aa 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -46,8 +46,9 @@ import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.prx.data.PRX_SERVICE_UUID +import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import no.nordicsemi.android.prx.repository.PRXRepository +import no.nordicsemi.android.prx.repository.PRX_SERVICE_UUID import no.nordicsemi.android.prx.view.DisconnectEvent import no.nordicsemi.android.prx.view.NavigateUpEvent import no.nordicsemi.android.prx.view.OpenLoggerEvent @@ -98,8 +99,8 @@ internal class PRXViewModel @Inject constructor( fun onEvent(event: PRXScreenViewEvent) { when (event) { DisconnectEvent -> disconnect() - TurnOffAlert -> repository.disableAlarm() - TurnOnAlert -> repository.enableAlarm() + TurnOffAlert -> repository.setRemoteAlarmLevel(AlarmLevel.NONE) + TurnOnAlert -> repository.setRemoteAlarmLevel(AlarmLevel.HIGH) NavigateUpEvent -> navigationManager.navigateUp() OpenLoggerEvent -> repository.openLogger() } From 4f0cdc08c965ac9813866ae082a161080b6c2f93 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 22 Mar 2023 10:56:50 +0100 Subject: [PATCH 023/101] Add alarm to PRX --- .../android/prx/data/PRXServiceData.kt | 9 +++++- .../android/prx/repository/AlarmHandler.kt | 4 +++ .../android/prx/repository/PRXRepository.kt | 29 +++---------------- .../android/prx/repository/PRXService.kt | 7 +++-- .../android/prx/viewmodel/PRXViewModel.kt | 19 +++++++++++- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt index 2c520815..47893a30 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -1,5 +1,6 @@ package no.nordicsemi.android.prx.data +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel @@ -8,6 +9,12 @@ data class PRXServiceData( val linkLossAlarmLevel: AlarmLevel = AlarmLevel.HIGH, val batteryLevel: Int? = null, val connectionState: GattConnectionState? = null, + val connectionStatus: BleGattConnectionStatus? = null, val isRemoteAlarm: Boolean = false, val deviceName: String? = null -) +) { + + val isLinkLossDisconnected = connectionState?.let { + connectionStatus == BleGattConnectionStatus.LINK_LOSS + } ?: false +} diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt index eccef2ee..cb909807 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt @@ -60,6 +60,10 @@ internal class AlarmHandler @Inject constructor( } fun playAlarm(alarmLevel: AlarmLevel) { + if (alarmLevel == AlarmLevel.NONE) { + pauseAlarm() + return + } val ringtone = when (alarmLevel) { AlarmLevel.NONE -> null AlarmLevel.MEDIUM -> mediumLevelRingtone diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 58acccbb..4fa5bd42 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -38,19 +38,13 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel -import no.nordicsemi.android.kotlin.ble.profile.prx.PRXData import no.nordicsemi.android.prx.data.PRXServiceData -import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.DisconnectAndStopEvent -import no.nordicsemi.android.service.LinkLossResult import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -58,8 +52,7 @@ import javax.inject.Singleton class PRXRepository @Inject internal constructor( @ApplicationContext private val context: Context, - private val serviceManager: ServiceManager, - private val alarmHandler: AlarmHandler, + private val serviceManager: ServiceManager ) { private val _data = MutableStateFlow(PRXServiceData()) @@ -81,8 +74,8 @@ class PRXRepository @Inject internal constructor( _data.value = _data.value.copy(deviceName = device.name) } - fun onConnectionStateChanged(connectionState: GattConnectionState?) { - _data.value = _data.value.copy(connectionState = connectionState) + fun onConnectionStateChanged(connection: Pair) { + _data.value = _data.value.copy(connectionState = connection.first, connectionStatus = connection.second) } fun setLocalAlarmLevel(alarmLevel: AlarmLevel) { @@ -93,20 +86,6 @@ class PRXRepository @Inject internal constructor( _data.value = _data.value.copy(linkLossAlarmLevel = alarmLevel) } - private fun handleLocalAlarm(result: BleManagerResult) { - (result as? SuccessResult)?.let { - if (it.data.localAlarmLevel != AlarmLevel.NONE) { - alarmHandler.playAlarm(it.data.localAlarmLevel) - } else { - alarmHandler.pauseAlarm() - } - } - (result as? LinkLossResult)?.let { - val alarmLevel = it.data?.linkLossAlarmLevel ?: AlarmLevel.HIGH - alarmHandler.playAlarm(alarmLevel) - } - } - fun onBatteryLevelChanged(batteryLevel: Int) { _data.value = _data.value.copy(batteryLevel = batteryLevel) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 707b0207..5991a39e 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -149,17 +149,18 @@ internal class PRXService : NotificationService() { linkLossCharacteristic.value .mapNotNull { AlarmLevelParser.parse(it) } - .onEach { repository.setLocalAlarmLevel(it) } + .onEach { repository.setLinkLossAlarmLevel(it) } .launchIn(lifecycleScope) } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { client = device.connect(this@PRXService) - client.connectionState + client.connectionStateWithStatus + .filterNotNull() .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() - .onEach { stopIfDisconnected(it) } + .onEach { stopIfDisconnected(it.first) } .launchIn(lifecycleScope) client.services diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index 080a86aa..588202dc 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -32,11 +32,14 @@ package no.nordicsemi.android.prx.viewmodel import android.os.ParcelUuid +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics @@ -47,6 +50,7 @@ import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel +import no.nordicsemi.android.prx.repository.AlarmHandler import no.nordicsemi.android.prx.repository.PRXRepository import no.nordicsemi.android.prx.repository.PRX_SERVICE_UUID import no.nordicsemi.android.prx.view.DisconnectEvent @@ -62,7 +66,8 @@ import javax.inject.Inject internal class PRXViewModel @Inject constructor( private val repository: PRXRepository, private val navigationManager: Navigator, - private val analytics: AppAnalytics + private val analytics: AppAnalytics, + private val alarmHandler: AlarmHandler ) : ViewModel() { val state = repository.data @@ -75,10 +80,21 @@ internal class PRXViewModel @Inject constructor( } repository.data.onEach { + Log.d("AAATESTAAA", "Data $it") + if (it.isLinkLossDisconnected) { + alarmHandler.playAlarm(it.linkLossAlarmLevel) + } + if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.PRX)) } }.launchIn(viewModelScope) + + repository.data + .map { it.localAlarmLevel } + .distinctUntilChanged() + .onEach { alarmHandler.playAlarm(it) } + .launchIn(viewModelScope) } private fun requestBluetoothDevice() { @@ -108,6 +124,7 @@ internal class PRXViewModel @Inject constructor( private fun disconnect() { repository.release() + alarmHandler.pauseAlarm() navigationManager.navigateUp() } } From 4522fc71c8d8246b1aba9320b20c96344b975a79 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 23 Mar 2023 14:01:55 +0100 Subject: [PATCH 024/101] Add link loss alarm --- .../no/nordicsemi/android/prx/data/PRXServiceData.kt | 6 ++++-- .../no/nordicsemi/android/prx/repository/PRXService.kt | 4 ++-- .../java/no/nordicsemi/android/prx/view/PRXScreen.kt | 10 +++++++++- .../nordicsemi/android/prx/viewmodel/PRXViewModel.kt | 7 +++++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt index 47893a30..e8f8529c 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -14,7 +14,9 @@ data class PRXServiceData( val deviceName: String? = null ) { - val isLinkLossDisconnected = connectionState?.let { - connectionStatus == BleGattConnectionStatus.LINK_LOSS + val isLinkLossDisconnected = connectionState?.let { connectionState -> + connectionStatus?.let { connectionStatus -> + connectionStatus != BleGattConnectionStatus.SUCCESS && connectionStatus != BleGattConnectionStatus.TERMINATE_PEER_USER + } ?: false } ?: false } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 5991a39e..c044bc67 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -33,7 +33,6 @@ package no.nordicsemi.android.prx.repository import android.annotation.SuppressLint import android.content.Intent -import android.util.Log import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.filterNotNull @@ -42,6 +41,7 @@ import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.BleGattConnectOptions import no.nordicsemi.android.kotlin.ble.core.client.BleWriteType import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristic @@ -154,7 +154,7 @@ internal class PRXService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@PRXService) + client = device.connect(this@PRXService, BleGattConnectOptions(autoConnect = true)) client.connectionStateWithStatus .filterNotNull() diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index 0a453206..e8716a52 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -78,7 +78,7 @@ fun PRXScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(getReason(state.isLinkLossDisconnected)) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> ContentView(state) { viewModel.onEvent(it) } } } @@ -86,6 +86,14 @@ fun PRXScreen() { } } +private fun getReason(isLinkLoss: Boolean): Reason { + return if (isLinkLoss) { + Reason.LINK_LOSS + } else { + Reason.UNKNOWN + } +} + @Composable private fun AppBar(state: PRXServiceData, navigateUp: () -> Unit, viewModel: PRXViewModel) { if (state.deviceName?.isNotBlank() == true) { diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index 588202dc..b56e7849 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -32,7 +32,6 @@ package no.nordicsemi.android.prx.viewmodel import android.os.ParcelUuid -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -80,7 +79,6 @@ internal class PRXViewModel @Inject constructor( } repository.data.onEach { - Log.d("AAATESTAAA", "Data $it") if (it.isLinkLossDisconnected) { alarmHandler.playAlarm(it.linkLossAlarmLevel) } @@ -127,4 +125,9 @@ internal class PRXViewModel @Inject constructor( alarmHandler.pauseAlarm() navigationManager.navigateUp() } + + override fun onCleared() { + super.onCleared() + alarmHandler.pauseAlarm() + } } From 496c8157138069a5f675bbcb6a10cefde15c4157 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 23 Mar 2023 15:14:39 +0100 Subject: [PATCH 025/101] Fix: connect client after server set up --- .../nordicsemi/android/prx/repository/PRXService.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index c044bc67..fdcbaa83 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -88,9 +88,7 @@ internal class PRXService : NotificationService() { val device = intent!!.getParcelableExtra(DEVICE_DATA)!! - startServer() - - startGattClient(device) + startServer(device) repository.stopEvent .onEach { disconnect() } @@ -103,7 +101,7 @@ internal class PRXService : NotificationService() { return START_REDELIVER_INTENT } - private fun startServer() { + private fun startServer(device: ServerDevice) = lifecycleScope.launch { val alertLevelCharacteristic = BleServerGattCharacteristicConfig( uuid = ALERT_LEVEL_CHARACTERISTIC_UUID, properties = listOf(BleGattProperty.PROPERTY_WRITE_NO_RESPONSE), @@ -130,6 +128,9 @@ internal class PRXService : NotificationService() { server = BleGattServer.create(this@PRXService, prxServiceConfig, linkLossServiceConfig) + //Order is important. We don't want to connect before services have been added to the server. + startGattClient(device) + server.onNewConnection .onEach { setUpServerConnection(it.second) } .launchIn(lifecycleScope) @@ -192,8 +193,9 @@ internal class PRXService : NotificationService() { repository.onRemoteAlarmLevelSet(alarmLevel) } - private fun stopIfDisconnected(connectionState: GattConnectionState) { + private suspend fun stopIfDisconnected(connectionState: GattConnectionState) { if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + server.stopServer() stopSelf() } } From fb1fdbf477a598deb06853a3240f02a61ae58e99 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 23 Mar 2023 15:37:52 +0100 Subject: [PATCH 026/101] Identify link loss signal --- .../java/no/nordicsemi/android/prx/data/PRXServiceData.kt | 6 +----- .../no/nordicsemi/android/prx/repository/PRXService.kt | 7 ++++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt index e8f8529c..36563a50 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -14,9 +14,5 @@ data class PRXServiceData( val deviceName: String? = null ) { - val isLinkLossDisconnected = connectionState?.let { connectionState -> - connectionStatus?.let { connectionStatus -> - connectionStatus != BleGattConnectionStatus.SUCCESS && connectionStatus != BleGattConnectionStatus.TERMINATE_PEER_USER - } ?: false - } ?: false + val isLinkLossDisconnected = connectionStatus?.isLinkLoss ?: false } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index fdcbaa83..d5b5862d 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -46,6 +46,7 @@ import no.nordicsemi.android.kotlin.ble.core.client.BleWriteType import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristic import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState @@ -161,7 +162,7 @@ internal class PRXService : NotificationService() { .filterNotNull() .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() - .onEach { stopIfDisconnected(it.first) } + .onEach { stopIfDisconnected(it.first, it.second) } .launchIn(lifecycleScope) client.services @@ -193,8 +194,8 @@ internal class PRXService : NotificationService() { repository.onRemoteAlarmLevelSet(alarmLevel) } - private suspend fun stopIfDisconnected(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + private fun stopIfDisconnected(connectionState: GattConnectionState, connectionStatus: BleGattConnectionStatus) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED && !connectionStatus.isLinkLoss) { server.stopServer() stopSelf() } From e4aabecccb8c1c71f91da0d95cae77275969133b Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 24 Mar 2023 13:08:08 +0100 Subject: [PATCH 027/101] Migrate UART profile to use the new library --- .../android/service/BleManagerStatus.kt | 67 ------- .../service/CloseableCoroutineScope.kt | 45 ----- .../service/ConnectionObserverAdapter.kt | 93 --------- .../android/prx/repository/PRXRepository.kt | 1 + .../android/prx/repository/PRXService.kt | 2 + profile_uart/build.gradle.kts | 1 + .../android/uart/data/UARTManager.kt | 189 ------------------ .../data/{UARTData.kt => UARTServiceData.kt} | 6 +- .../android/uart/repository/UARTRepository.kt | 86 ++++---- .../android/uart/repository/UARTService.kt | 79 +++++++- .../android/uart/view/UARTContentView.kt | 4 +- .../android/uart/view/UARTScreen.kt | 59 ++---- .../nordicsemi/android/uart/view/UARTState.kt | 13 +- .../android/uart/viewmodel/UARTViewModel.kt | 13 +- 14 files changed, 149 insertions(+), 509 deletions(-) delete mode 100644 lib_service/src/main/java/no/nordicsemi/android/service/BleManagerStatus.kt delete mode 100644 lib_service/src/main/java/no/nordicsemi/android/service/CloseableCoroutineScope.kt delete mode 100644 lib_service/src/main/java/no/nordicsemi/android/service/ConnectionObserverAdapter.kt delete mode 100644 profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTManager.kt rename profile_uart/src/main/java/no/nordicsemi/android/uart/data/{UARTData.kt => UARTServiceData.kt} (89%) diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/BleManagerStatus.kt b/lib_service/src/main/java/no/nordicsemi/android/service/BleManagerStatus.kt deleted file mode 100644 index c533b04d..00000000 --- a/lib_service/src/main/java/no/nordicsemi/android/service/BleManagerStatus.kt +++ /dev/null @@ -1,67 +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.service - -import android.annotation.SuppressLint -import android.bluetooth.BluetoothDevice - -sealed interface BleManagerResult { - - fun isRunning(): Boolean { - return this is SuccessResult - } - - fun hasBeenDisconnected(): Boolean { - return this is LinkLossResult || this is DisconnectedResult || this is MissingServiceResult - } - - fun hasBeenDisconnectedWithoutLinkLoss(): Boolean { - return this is DisconnectedResult || this is MissingServiceResult - } -} - -sealed class DeviceHolder(val device: BluetoothDevice) { - - @SuppressLint("MissingPermission") - fun deviceName(): String = device.name ?: device.address - -} - -class IdleResult : BleManagerResult -class ConnectingResult(device: BluetoothDevice) : DeviceHolder(device), BleManagerResult -class ConnectedResult(device: BluetoothDevice) : DeviceHolder(device), BleManagerResult -class SuccessResult(device: BluetoothDevice, val data: T) : DeviceHolder(device), BleManagerResult - -class LinkLossResult(device: BluetoothDevice, val data: T?) : DeviceHolder(device), BleManagerResult -class DisconnectedResult(device: BluetoothDevice) : DeviceHolder(device), BleManagerResult -class UnknownErrorResult(device: BluetoothDevice) : DeviceHolder(device), BleManagerResult -class MissingServiceResult(device: BluetoothDevice) : DeviceHolder(device), BleManagerResult diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/CloseableCoroutineScope.kt b/lib_service/src/main/java/no/nordicsemi/android/service/CloseableCoroutineScope.kt deleted file mode 100644 index da30f631..00000000 --- a/lib_service/src/main/java/no/nordicsemi/android/service/CloseableCoroutineScope.kt +++ /dev/null @@ -1,45 +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.service - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.cancelChildren -import java.io.Closeable -import kotlin.coroutines.CoroutineContext - -class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope { - override val coroutineContext: CoroutineContext = context - - override fun close() { - coroutineContext.cancelChildren() - } -} diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/ConnectionObserverAdapter.kt b/lib_service/src/main/java/no/nordicsemi/android/service/ConnectionObserverAdapter.kt deleted file mode 100644 index 87c6de7c..00000000 --- a/lib_service/src/main/java/no/nordicsemi/android/service/ConnectionObserverAdapter.kt +++ /dev/null @@ -1,93 +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.service - -import android.bluetooth.BluetoothDevice -import android.util.Log -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import no.nordicsemi.android.ble.observer.ConnectionObserver - -class ConnectionObserverAdapter : ConnectionObserver { - - private val TAG = "BLE-CONNECTION" - - private val _status = MutableStateFlow>(IdleResult()) - val status = _status.asStateFlow() - - private var lastValue: T? = null - - private fun getData(): T? { - return (_status.value as? SuccessResult)?.data - } - - override fun onDeviceConnecting(device: BluetoothDevice) { - Log.d(TAG, "onDeviceConnecting()") - _status.value = ConnectingResult(device) - } - - override fun onDeviceConnected(device: BluetoothDevice) { - Log.d(TAG, "onDeviceConnected()") - _status.value = ConnectedResult(device) - } - - override fun onDeviceFailedToConnect(device: BluetoothDevice, reason: Int) { - Log.d(TAG, "onDeviceFailedToConnect(), reason: $reason") - _status.value = MissingServiceResult(device) - } - - override fun onDeviceReady(device: BluetoothDevice) { - Log.d(TAG, "onDeviceReady()") - _status.value = SuccessResult(device, lastValue!!) - } - - override fun onDeviceDisconnecting(device: BluetoothDevice) { - Log.d(TAG, "onDeviceDisconnecting()") - } - - override fun onDeviceDisconnected(device: BluetoothDevice, reason: Int) { - Log.d(TAG, "onDeviceDisconnected(), reason: $reason") - _status.value = when (reason) { - ConnectionObserver.REASON_NOT_SUPPORTED -> MissingServiceResult(device) - ConnectionObserver.REASON_LINK_LOSS -> LinkLossResult(device, getData()) - ConnectionObserver.REASON_SUCCESS -> DisconnectedResult(device) - else -> UnknownErrorResult(device) - } - } - - fun setValue(value: T) { - lastValue = value - (_status.value as? SuccessResult)?.let { - _status.value = SuccessResult(it.device, value) - } - } -} diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 4fa5bd42..4add8c10 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -103,6 +103,7 @@ class PRXRepository @Inject internal constructor( } fun release() { + _data.value = PRXServiceData() _remoteAlarmLevel.tryEmit(AlarmLevel.NONE) _stopEvent.tryEmit(DisconnectAndStopEvent()) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index d5b5862d..55c6d953 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -33,6 +33,7 @@ package no.nordicsemi.android.prx.repository import android.annotation.SuppressLint import android.content.Intent +import android.util.Log import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.filterNotNull @@ -197,6 +198,7 @@ internal class PRXService : NotificationService() { private fun stopIfDisconnected(connectionState: GattConnectionState, connectionStatus: BleGattConnectionStatus) { if (connectionState == GattConnectionState.STATE_DISCONNECTED && !connectionStatus.isLinkLoss) { server.stopServer() + repository.release() stopSelf() } } diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index 8890e92e..aaec288c 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -68,6 +68,7 @@ dependencies { implementation(libs.nordic.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.nordic.core) implementation(libs.androidx.dataStore.core) implementation(libs.androidx.dataStore.preferences) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTManager.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTManager.kt deleted file mode 100644 index fe0c165f..00000000 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTManager.kt +++ /dev/null @@ -1,189 +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.uart.data - -import android.annotation.SuppressLint -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCharacteristic -import android.bluetooth.BluetoothGattService -import android.content.Context -import android.util.Log -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.* -import no.nordicsemi.android.ble.BleManager -import no.nordicsemi.android.ble.WriteRequest -import no.nordicsemi.android.ble.common.callback.battery.BatteryLevelResponse -import no.nordicsemi.android.ble.ktx.asFlow -import no.nordicsemi.android.ble.ktx.asValidResponseFlow -import no.nordicsemi.android.ble.ktx.suspend -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.service.ConnectionObserverAdapter -import no.nordicsemi.android.utils.EMPTY -import no.nordicsemi.android.utils.launchWithCatch -import java.util.* - -val UART_SERVICE_UUID: UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E") -private val UART_RX_CHARACTERISTIC_UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E") -private val UART_TX_CHARACTERISTIC_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E") - -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 UARTManager( - context: Context, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : BleManager(context) { - - private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null - - private var rxCharacteristic: BluetoothGattCharacteristic? = null - private var txCharacteristic: BluetoothGattCharacteristic? = null - - private var useLongWrite = true - - private val data = MutableStateFlow(UARTData()) - val dataHolder = ConnectionObserverAdapter() - - init { - connectionObserver = dataHolder - - data.onEach { - dataHolder.setValue(it) - }.launchIn(scope) - } - - override fun log(priority: Int, message: String) { - logger.log(priority, message) - } - - override fun getMinLogPriority(): Int { - return Log.VERBOSE - } - - private inner class UARTManagerGattCallback : BleManagerGattCallback() { - - @SuppressLint("WrongConstant") - override fun initialize() { - setNotificationCallback(txCharacteristic).asFlow() - .flowOn(Dispatchers.IO) - .map { - val text: String = it.getStringValue(0) ?: String.EMPTY - log(10, "\"$text\" received") - val messages = data.value.messages + UARTRecord(text, UARTRecordType.OUTPUT) - messages.takeLast(50) - } - .onEach { - data.value = data.value.copy(messages = it) - }.launchIn(scope) - - requestMtu(517).enqueue() - enableNotifications(txCharacteristic).enqueue() - - setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow() - .onEach { - data.value = data.value.copy(batteryLevel = it.batteryLevel) - }.launchIn(scope) - enableNotifications(batteryLevelCharacteristic).enqueue() - } - - override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - val service: BluetoothGattService? = gatt.getService(UART_SERVICE_UUID) - if (service != null) { - rxCharacteristic = service.getCharacteristic(UART_RX_CHARACTERISTIC_UUID) - txCharacteristic = service.getCharacteristic(UART_TX_CHARACTERISTIC_UUID) - } - var writeRequest = false - var writeCommand = false - - rxCharacteristic?.let { - val rxProperties: Int = it.properties - writeRequest = rxProperties and BluetoothGattCharacteristic.PROPERTY_WRITE > 0 - writeCommand = - rxProperties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE > 0 - - // Set the WRITE REQUEST type when the characteristic supports it. - // This will allow to send long write (also if the characteristic support it). - // In case there is no WRITE REQUEST property, this manager will divide texts - // longer then MTU-3 bytes into up to MTU-3 bytes chunks. - if (!writeRequest) { - useLongWrite = false - } - } - gatt.getService(BATTERY_SERVICE_UUID)?.run { - batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID) - } - return rxCharacteristic != null && txCharacteristic != null && (writeRequest || writeCommand) - } - - override fun onServicesInvalidated() { - batteryLevelCharacteristic = null - rxCharacteristic = null - txCharacteristic = null - useLongWrite = true - } - } - - @SuppressLint("WrongConstant") - fun send(text: String) { - if (rxCharacteristic == null) return - scope.launchWithCatch { - val writeType = if (useLongWrite) { - BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - } else { - BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE - } - val request: WriteRequest = - writeCharacteristic(rxCharacteristic, text.toByteArray(), writeType) - if (!useLongWrite) { - request.split() - } - request.suspend() - data.value = data.value.copy( - messages = data.value.messages + UARTRecord( - text, - UARTRecordType.INPUT - ) - ) - log(10, "\"$text\" sent") - } - } - - fun clearItems() { - data.value = data.value.copy(messages = emptyList()) - } - - override fun getGattCallback(): BleManagerGattCallback { - return UARTManagerGattCallback() - } -} diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTData.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt similarity index 89% rename from profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTData.kt rename to profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt index a1e8722d..162b1307 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTData.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt @@ -31,9 +31,13 @@ package no.nordicsemi.android.uart.data -internal data class UARTData( +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState + +internal data class UARTServiceData( val messages: List = emptyList(), + val connectionState: GattConnectionState = GattConnectionState.STATE_DISCONNECTED, val batteryLevel: Int? = null, + val deviceName: String? = null ) { val displayMessages = messages diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 47deff88..19827880 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -33,27 +33,23 @@ package no.nordicsemi.android.uart.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.service.IdleResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.uart.data.ConfigurationDataSource import no.nordicsemi.android.uart.data.MacroEol -import no.nordicsemi.android.uart.data.UARTData import no.nordicsemi.android.uart.data.UARTMacro -import no.nordicsemi.android.uart.data.UARTManager +import no.nordicsemi.android.uart.data.UARTRecord +import no.nordicsemi.android.uart.data.UARTRecordType +import no.nordicsemi.android.uart.data.UARTServiceData import no.nordicsemi.android.uart.data.parseWithNewLineChar -import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.android.utils.EMPTY import javax.inject.Inject import javax.inject.Singleton @@ -62,18 +58,20 @@ class UARTRepository @Inject internal constructor( @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val configurationDataSource: ConfigurationDataSource, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst + private val configurationDataSource: ConfigurationDataSource ) { - private var manager: UARTManager? = null private var logger: NordicLogger? = null - private val _data = MutableStateFlow>(IdleResult()) + private val _data = MutableStateFlow(UARTServiceData()) internal val data = _data.asStateFlow() - val isRunning = data.map { it.isRunning() } - val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } + private val _stopEvent = simpleSharedFlow() + internal val stopEvent = _stopEvent.asSharedFlow() + + private val _command = simpleSharedFlow() + internal val command = _command.asSharedFlow() + + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } val lastConfigurationName = configurationDataSource.lastConfigurationName @@ -81,33 +79,39 @@ class UARTRepository @Inject internal constructor( serviceManager.startService(UARTService::class.java, device) } - fun start(device: ServerDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "UART", device.address).also { - logger = it - } - val manager = UARTManager(context, scope, createdLogger) - this.manager = manager + fun onConnectionStateChanged(connectionState: GattConnectionState) { + _data.value = _data.value.copy(connectionState = connectionState) + } - manager.dataHolder.status.onEach { - _data.value = it - }.launchIn(scope) + fun onBatteryLevelChanged(batteryLevel: Int) { + _data.value = _data.value.copy(batteryLevel = batteryLevel) + } - scope.launch { - manager.start(device) - } + fun onNewMessageReceived(value: String) { + _data.value = _data.value.copy(messages = _data.value.messages + UARTRecord(value, UARTRecordType.OUTPUT)) + } + + fun onNewMessageSent(value: String) { + _data.value = _data.value.copy(messages = _data.value.messages + UARTRecord(value, UARTRecordType.INPUT)) + } + + fun onInitComplete(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) } fun sendText(text: String, newLineChar: MacroEol) { - manager?.send(text.parseWithNewLineChar(newLineChar)) + _command.tryEmit(text.parseWithNewLineChar(newLineChar)) } fun runMacro(macro: UARTMacro) { - val command = macro.command?.parseWithNewLineChar(macro.newLineChar) - manager?.send(command ?: String.EMPTY) + if (macro.command == null) { + return + } + _command.tryEmit(macro.command.parseWithNewLineChar(macro.newLineChar)) } fun clearItems() { - manager?.clearItems() + _data.value = _data.value.copy(messages = emptyList()) } fun openLogger() { @@ -118,20 +122,8 @@ class UARTRepository @Inject internal constructor( configurationDataSource.saveConfigurationName(name) } - private suspend fun UARTManager.start(device: ServerDevice) { -// try { -// connect(device.device) -// .useAutoConnect(false) -// .retry(3, 100) -// .suspend() -// } catch (e: Exception) { -// e.printStackTrace() -// } - } - fun release() { - manager?.disconnect()?.enqueue() - manager = null + _stopEvent.tryEmit(DisconnectAndStopEvent()) logger = null } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 611fa329..e5d93161 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -31,33 +31,104 @@ package no.nordicsemi.android.uart.repository +import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import java.util.* import javax.inject.Inject +val UART_SERVICE_UUID: UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E") +private val UART_RX_CHARACTERISTIC_UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E") +private val UART_TX_CHARACTERISTIC_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E") + +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") + +@SuppressLint("MissingPermission") @AndroidEntryPoint internal class UARTService : NotificationService() { @Inject lateinit var repository: UARTRepository + private lateinit var client: BleGattClient + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) val device = intent!!.getParcelableExtra(DEVICE_DATA)!! - repository.start(device, lifecycleScope) + startGattClient(device) - repository.hasBeenDisconnected.onEach { - if (it) stopSelf() - }.launchIn(lifecycleScope) + repository.stopEvent + .onEach { disconnect() } + .launchIn(lifecycleScope) return START_REDELIVER_INTENT } + + private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { + client = device.connect(this@UARTService) + + client.connectionState + .onEach { repository.onConnectionStateChanged(it) } + .filterNotNull() + .onEach { stopIfDisconnected(it) } + .launchIn(lifecycleScope) + + client.services + .filterNotNull() + .onEach { configureGatt(it, device) } + .launchIn(lifecycleScope) + + client.requestMtu(517) + } + + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + val uartService = services.findService(UART_SERVICE_UUID)!! + val rxCharacteristic = uartService.findCharacteristic(UART_RX_CHARACTERISTIC_UUID)!! + val txCharacteristic = uartService.findCharacteristic(UART_TX_CHARACTERISTIC_UUID)!! + + val batteryService = services.findService(BATTERY_SERVICE_UUID) + + batteryService?.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)?.getNotifications() + ?.mapNotNull { BatteryLevelParser.parse(it) } + ?.onEach { repository.onBatteryLevelChanged(it) } + ?.launchIn(lifecycleScope) + + txCharacteristic.getNotifications() + .onEach { repository.onNewMessageReceived(String(it)) } + .launchIn(lifecycleScope) + + repository.command + .onEach { rxCharacteristic.write(it.toByteArray()) } + .onEach { repository.onNewMessageSent(it) } + .launchIn(lifecycleScope) + + repository.onInitComplete(device) + } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + stopSelf() + } + } + + private fun disconnect() { + client.disconnect() + } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTContentView.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTContentView.kt index 5527a910..c918480c 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTContentView.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTContentView.kt @@ -42,12 +42,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import no.nordicsemi.android.uart.data.UARTData +import no.nordicsemi.android.uart.data.UARTServiceData import no.nordicsemi.android.ui.view.ScreenSection @Composable internal fun UARTContentView( - state: UARTData, + state: UARTServiceData, onEvent: (UARTViewEvent) -> Unit ) { Column( diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index 87f0b7d3..39d9f159 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -54,15 +54,7 @@ import no.nordicsemi.android.common.theme.view.PagerViewItem import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult -import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.viewmodel.UARTViewModel import no.nordicsemi.android.ui.view.BackIconAppBar @@ -78,22 +70,19 @@ fun UARTScreen() { val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( - topBar = { AppBar(state, navigateUp) { viewModel.onEvent(it) } } + topBar = { AppBar(state, navigateUp, viewModel) } ) { Column( modifier = Modifier.padding(it) ) { - when (state.uartManagerState) { - NoDeviceState -> PaddingBox { DeviceConnectingView() } - is WorkingState -> when (state.uartManagerState.result) { - is IdleResult, - is ConnectingResult -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } - is ConnectedResult -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } - is DisconnectedResult -> PaddingBox { DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } } - is LinkLossResult -> PaddingBox { DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) } } - is MissingServiceResult -> PaddingBox { DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) } } - is UnknownErrorResult -> PaddingBox { DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } } - is SuccessResult -> SuccessScreen() + if (state.uartManagerState.deviceName == null) { + DeviceConnectingView() + } else { + when (state.uartManagerState.connectionState) { + GattConnectionState.STATE_CONNECTING -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> SuccessScreen() } } } @@ -108,17 +97,13 @@ private fun PaddingBox(content: @Composable () -> Unit) { } @Composable -private fun AppBar(state: UARTViewState, navigateUp: () -> Unit, onEvent: (UARTViewEvent) -> Unit) { - val toolbarName = (state.uartManagerState as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { - BackIconAppBar(stringResource(id = R.string.uart_title), navigateUp) - } else { - LoggerIconAppBar(toolbarName, navigateUp, { onEvent(DisconnectEvent) }) { - onEvent(OpenLogger) +private fun AppBar(state: UARTViewState, navigateUp: () -> Unit, viewModel: UARTViewModel) { + if (state.uartManagerState.deviceName?.isNotBlank() == true) { + LoggerIconAppBar(state.uartManagerState.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { + viewModel.onEvent(OpenLogger) } + } else { + BackIconAppBar(stringResource(id = R.string.uart_title), navigateUp) } } @@ -146,22 +131,14 @@ private fun SuccessScreen() { private fun KeyboardView() { val viewModel: UARTViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - (state.uartManagerState as? WorkingState)?.let { - (state.uartManagerState.result as? SuccessResult)?.let { - UARTContentView(it.data) { viewModel.onEvent(it) } - } - } + UARTContentView(state.uartManagerState) { viewModel.onEvent(it) } } @Composable private fun MacroView() { val viewModel: UARTViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - (state.uartManagerState as? WorkingState)?.let { - (state.uartManagerState.result as? SuccessResult)?.let { - MacroSection(state) { viewModel.onEvent(it) } - } - } + MacroSection(state) { viewModel.onEvent(it) } } @Composable diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt index 0cd0bfa1..ff9fe7ad 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt @@ -31,9 +31,8 @@ package no.nordicsemi.android.uart.view -import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.uart.data.UARTConfiguration -import no.nordicsemi.android.uart.data.UARTData +import no.nordicsemi.android.uart.data.UARTServiceData import no.nordicsemi.android.uart.data.UARTMacro internal data class UARTViewState( @@ -41,7 +40,7 @@ internal data class UARTViewState( val selectedConfigurationName: String? = null, val isConfigurationEdited: Boolean = false, val configurations: List = emptyList(), - val uartManagerState: HTSManagerState = NoDeviceState, + val uartManagerState: UARTServiceData = UARTServiceData(), val isInputVisible: Boolean = true ) { val showEditDialog: Boolean = editedPosition != null @@ -54,11 +53,3 @@ internal data class UARTViewState( } } } - -internal sealed class HTSManagerState - -internal data class WorkingState( - val result: BleManagerResult -) : HTSManagerState() - -internal object NoDeviceState : HTSManagerState() diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index 9e3a0409..69dce4b8 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -52,15 +52,14 @@ import no.nordicsemi.android.analytics.UARTSendAnalyticsEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.IdleResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.uart.data.MacroEol import no.nordicsemi.android.uart.data.UARTConfiguration import no.nordicsemi.android.uart.data.UARTMacro import no.nordicsemi.android.uart.data.UARTPersistentDataSource -import no.nordicsemi.android.uart.data.UART_SERVICE_UUID import no.nordicsemi.android.uart.repository.UARTRepository +import no.nordicsemi.android.uart.repository.UART_SERVICE_UUID import no.nordicsemi.android.uart.view.ClearOutputItems import no.nordicsemi.android.uart.view.DisconnectEvent import no.nordicsemi.android.uart.view.MacroInputSwitchClick @@ -78,7 +77,6 @@ import no.nordicsemi.android.uart.view.OnRunMacro import no.nordicsemi.android.uart.view.OpenLogger import no.nordicsemi.android.uart.view.UARTViewEvent import no.nordicsemi.android.uart.view.UARTViewState -import no.nordicsemi.android.uart.view.WorkingState import javax.inject.Inject @HiltViewModel @@ -100,12 +98,9 @@ internal class UARTViewModel @Inject constructor( } repository.data.onEach { - if (it is IdleResult) { - return@onEach - } - _state.value = _state.value.copy(uartManagerState = WorkingState(it)) + _state.value = _state.value.copy(uartManagerState = it) - (it as? ConnectedResult)?.let { + if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.UART)) } }.launchIn(viewModelScope) From bad5f296dc917fb26993df68be0e6a54d5adab61 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 28 Mar 2023 10:39:56 +0200 Subject: [PATCH 028/101] Fix project after BLEK refactoring --- profile_bps/build.gradle.kts | 2 +- .../android/bps/viewmodel/BPSViewModel.kt | 5 +++-- profile_cgms/build.gradle.kts | 2 +- .../android/cgms/repository/CGMService.kt | 7 +++--- profile_csc/build.gradle.kts | 2 +- .../android/csc/repository/CSCService.kt | 5 +++-- profile_gls/build.gradle.kts | 2 +- .../gls/main/viewmodel/GLSViewModel.kt | 7 +++--- profile_hrs/build.gradle.kts | 2 +- .../android/hrs/service/HRSService.kt | 5 +++-- profile_hts/build.gradle.kts | 2 +- .../android/hts/repository/HTSService.kt | 5 +++-- profile_prx/build.gradle.kts | 3 ++- .../android/prx/repository/PRXService.kt | 22 +++++++++---------- profile_rscs/build.gradle.kts | 2 +- .../android/rscs/repository/RSCSService.kt | 5 +++-- profile_uart/build.gradle.kts | 2 +- .../android/uart/repository/UARTService.kt | 6 ++--- 18 files changed, 47 insertions(+), 39 deletions(-) diff --git a/profile_bps/build.gradle.kts b/profile_bps/build.gradle.kts index 39c04e70..44a19433 100644 --- a/profile_bps/build.gradle.kts +++ b/profile_bps/build.gradle.kts @@ -45,7 +45,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.nordic.ble.common) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 615d69d1..560d91b1 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -54,9 +54,10 @@ import no.nordicsemi.android.bps.view.DisconnectEvent import no.nordicsemi.android.bps.view.OpenLoggerEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator +import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.bps.data.BloodPressureMeasurementData diff --git a/profile_cgms/build.gradle.kts b/profile_cgms/build.gradle.kts index 204279e2..b5af38be 100644 --- a/profile_cgms/build.gradle.kts +++ b/profile_cgms/build.gradle.kts @@ -45,7 +45,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.nordic.ble.common) 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 c0aa46d9..bd5512f6 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 @@ -43,10 +43,11 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.ble.common.data.cgm.CGMSpecificOpsControlPointData import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber import no.nordicsemi.android.cgms.data.CGMServiceCommand +import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristic -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.cgm.CGMFeatureParser diff --git a/profile_csc/build.gradle.kts b/profile_csc/build.gradle.kts index 016ed175..6549f56a 100644 --- a/profile_csc/build.gradle.kts +++ b/profile_csc/build.gradle.kts @@ -45,7 +45,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.nordic.ble.common) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 181abbd0..3058f5c0 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -40,9 +40,10 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.csc.CSCDataParser diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index ed4598d9..03bd4f2a 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -45,7 +45,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.chart) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 14feaa78..e93f3003 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -58,10 +58,11 @@ import no.nordicsemi.android.gls.main.view.GLSViewState import no.nordicsemi.android.gls.main.view.OnGLSRecordClick import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected import no.nordicsemi.android.gls.main.view.OpenLoggerEvent +import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristic -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementContextParser diff --git a/profile_hrs/build.gradle.kts b/profile_hrs/build.gradle.kts index 470c8a69..3915e9ac 100644 --- a/profile_hrs/build.gradle.kts +++ b/profile_hrs/build.gradle.kts @@ -45,7 +45,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.chart) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 4240f176..53791efa 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -40,9 +40,10 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.hrs.BodySensorLocationParser diff --git a/profile_hts/build.gradle.kts b/profile_hts/build.gradle.kts index 66296ad1..2f90cf11 100644 --- a/profile_hts/build.gradle.kts +++ b/profile_hts/build.gradle.kts @@ -45,7 +45,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.nordic.ble.common) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 7094c524..f4b37f4b 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -40,9 +40,10 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.hts.HTSDataParser diff --git a/profile_prx/build.gradle.kts b/profile_prx/build.gradle.kts index 58cea4fe..e59fb25d 100644 --- a/profile_prx/build.gradle.kts +++ b/profile_prx/build.gradle.kts @@ -45,7 +45,8 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:server:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.nordic.ble.common) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 55c6d953..7f95f71a 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -33,7 +33,6 @@ package no.nordicsemi.android.prx.repository import android.annotation.SuppressLint import android.content.Intent -import android.util.Log import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.filterNotNull @@ -41,25 +40,26 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.client.BleGattConnectOptions -import no.nordicsemi.android.kotlin.ble.core.client.BleWriteType -import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattCharacteristic -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectOptions import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty +import no.nordicsemi.android.kotlin.ble.core.data.BleWriteType import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.core.server.BleGattServer -import no.nordicsemi.android.kotlin.ble.core.server.service.service.BleGattServerServiceType -import no.nordicsemi.android.kotlin.ble.core.server.service.service.BleServerGattCharacteristicConfig -import no.nordicsemi.android.kotlin.ble.core.server.service.service.BleServerGattServiceConfig -import no.nordicsemi.android.kotlin.ble.core.server.service.service.BluetoothGattServerConnection import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevelParser import no.nordicsemi.android.kotlin.ble.profile.prx.AlertLevelInputParser +import no.nordicsemi.android.kotlin.ble.server.main.BleGattServer +import no.nordicsemi.android.kotlin.ble.server.main.service.BleGattServerServiceType +import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristicConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattServiceConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.BluetoothGattServerConnection import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import java.util.* diff --git a/profile_rscs/build.gradle.kts b/profile_rscs/build.gradle.kts index 513505bc..f02e760a 100644 --- a/profile_rscs/build.gradle.kts +++ b/profile_rscs/build.gradle.kts @@ -45,7 +45,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.nordic.ble.common) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index e29b9b1e..b633cf96 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -40,9 +40,10 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSDataParser diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index aaec288c..edec1caa 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -51,7 +51,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") implementation(libs.room.runtime) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index e5d93161..adbfa795 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -40,12 +40,12 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser -import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import java.util.* From 28c41bcd93a5a09eb65c3222bb9cbd6425929449 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 29 Mar 2023 09:33:56 +0200 Subject: [PATCH 029/101] Add long write --- .../android/uart/repository/UARTService.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index adbfa795..a6d42640 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -42,8 +42,11 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty +import no.nordicsemi.android.kotlin.ble.core.data.BleWriteType import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.service.DEVICE_DATA @@ -115,13 +118,21 @@ internal class UARTService : NotificationService() { .launchIn(lifecycleScope) repository.command - .onEach { rxCharacteristic.write(it.toByteArray()) } + .onEach { rxCharacteristic.write(it.toByteArray(), getWriteType(rxCharacteristic)) } .onEach { repository.onNewMessageSent(it) } .launchIn(lifecycleScope) repository.onInitComplete(device) } + private fun getWriteType(characteristic: BleGattCharacteristic): BleWriteType { + return if (characteristic.properties.contains(BleGattProperty.PROPERTY_WRITE)) { + BleWriteType.DEFAULT + } else { + BleWriteType.NO_RESPONSE + } + } + private fun stopIfDisconnected(connectionState: GattConnectionState) { if (connectionState == GattConnectionState.STATE_DISCONNECTED) { stopSelf() From 55d34013f1c54875e70b48c47111a1077fa036a8 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 29 Mar 2023 09:43:50 +0200 Subject: [PATCH 030/101] Fix padding on UART --- .../src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index 39d9f159..77334b90 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -76,7 +76,7 @@ fun UARTScreen() { modifier = Modifier.padding(it) ) { if (state.uartManagerState.deviceName == null) { - DeviceConnectingView() + PaddingBox { DeviceConnectingView() } } else { when (state.uartManagerState.connectionState) { GattConnectionState.STATE_CONNECTING -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } From 0309932f0da64f345b2e47b693879ea3f03584c2 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 29 Mar 2023 15:59:49 +0200 Subject: [PATCH 031/101] Add split write to nRF Toolbox --- profile_uart/build.gradle.kts | 1 + .../java/no/nordicsemi/android/uart/repository/UARTService.kt | 4 +--- .../main/java/no/nordicsemi/android/uart/view/UARTScreen.kt | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index edec1caa..17a318aa 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -51,6 +51,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index a6d42640..f03fc378 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -97,8 +97,6 @@ internal class UARTService : NotificationService() { .filterNotNull() .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) - - client.requestMtu(517) } private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { @@ -118,7 +116,7 @@ internal class UARTService : NotificationService() { .launchIn(lifecycleScope) repository.command - .onEach { rxCharacteristic.write(it.toByteArray(), getWriteType(rxCharacteristic)) } + .onEach { rxCharacteristic.splitWrite(it.toByteArray(), getWriteType(rxCharacteristic)) } .onEach { repository.onNewMessageSent(it) } .launchIn(lifecycleScope) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index 77334b90..b7583647 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -81,7 +81,7 @@ fun UARTScreen() { when (state.uartManagerState.connectionState) { GattConnectionState.STATE_CONNECTING -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTING -> PaddingBox { DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } } GattConnectionState.STATE_CONNECTED -> SuccessScreen() } } From a431df5e78dca550369f968f4461cc6cda04a46a Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 30 Mar 2023 16:18:32 +0200 Subject: [PATCH 032/101] Add logger --- .../no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt | 9 +++++++-- .../no/nordicsemi/android/cgms/repository/CGMService.kt | 9 ++++++++- .../no/nordicsemi/android/csc/repository/CSCService.kt | 9 ++++++++- .../android/gls/main/viewmodel/GLSViewModel.kt | 9 +++++++-- .../java/no/nordicsemi/android/hrs/service/HRSService.kt | 9 ++++++++- .../no/nordicsemi/android/hts/repository/HTSService.kt | 9 ++++++++- .../no/nordicsemi/android/prx/repository/PRXService.kt | 9 ++++++++- .../no/nordicsemi/android/rscs/repository/RSCSService.kt | 9 ++++++++- .../no/nordicsemi/android/uart/repository/UARTService.kt | 9 ++++++++- 9 files changed, 70 insertions(+), 11 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 560d91b1..a369ffef 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -52,6 +52,7 @@ import no.nordicsemi.android.bps.view.BPSViewEvent import no.nordicsemi.android.bps.view.BPSViewState import no.nordicsemi.android.bps.view.DisconnectEvent import no.nordicsemi.android.bps.view.OpenLoggerEvent +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient @@ -65,6 +66,7 @@ import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementPars import no.nordicsemi.android.kotlin.ble.profile.bps.data.IntermediateCuffPressureData import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureParser import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId +import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -81,7 +83,8 @@ internal class BPSViewModel @Inject constructor( @ApplicationContext private val context: Context, private val navigationManager: Navigator, - private val analytics: AppAnalytics + private val analytics: AppAnalytics, + private val stringConst: StringConst ) : ViewModel() { private val _state = MutableStateFlow(BPSViewState()) @@ -114,7 +117,9 @@ internal class BPSViewModel @Inject constructor( private fun startGattClient(device: ServerDevice) = viewModelScope.launch { _state.value = _state.value.copy(deviceName = device.name) - client = device.connect(context) + val logger = NordicLogger(context, stringConst.APP_NAME, "BPS", device.address) + + client = device.connect(context, logger = logger) client.connectionState .filterNotNull() 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 bd5512f6..5084fc93 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 @@ -43,6 +43,7 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.ble.common.data.cgm.CGMSpecificOpsControlPointData import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber import no.nordicsemi.android.cgms.data.CGMServiceCommand +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic @@ -66,6 +67,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.ui.view.StringConst import no.nordicsemi.android.utils.launchWithCatch import java.util.* import javax.inject.Inject @@ -88,6 +90,9 @@ internal class CGMService : NotificationService() { @Inject lateinit var repository: CGMRepository + @Inject + lateinit var stringConst: StringConst + private lateinit var client: BleGattClient private var secured = false @@ -126,7 +131,9 @@ internal class CGMService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@CGMService) + val logger = NordicLogger(this@CGMService, stringConst.APP_NAME, "CGM", device.address) + + client = device.connect(this@CGMService, logger = logger) client.connectionState .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 3058f5c0..2f218f51 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -49,6 +50,7 @@ import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.csc.CSCDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -65,6 +67,9 @@ internal class CSCService : NotificationService() { @Inject lateinit var repository: CSCRepository + @Inject + lateinit var stringConst: StringConst + private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -82,7 +87,9 @@ internal class CSCService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@CSCService) + val logger = NordicLogger(this@CSCService, stringConst.APP_NAME, "CSC", device.address) + + client = device.connect(this@CSCService, logger = logger) client.connectionState .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index e93f3003..7ff5f277 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -48,6 +48,7 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.GlsDetailsDestinationId @@ -77,6 +78,7 @@ import no.nordicsemi.android.kotlin.ble.profile.gls.data.ResponseData import no.nordicsemi.android.kotlin.ble.profile.racp.RACPOpCode import no.nordicsemi.android.kotlin.ble.profile.racp.RACPResponseCode import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId +import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -96,7 +98,8 @@ internal class GLSViewModel @Inject constructor( @ApplicationContext private val context: Context, private val navigationManager: Navigator, - private val analytics: AppAnalytics + private val analytics: AppAnalytics, + private val stringConst: StringConst ) : ViewModel() { private lateinit var client: BleGattClient @@ -153,7 +156,9 @@ internal class GLSViewModel @Inject constructor( } private fun startGattClient(device: ServerDevice) = viewModelScope.launch { - client = device.connect(context) + val logger = NordicLogger(context, stringConst.APP_NAME, "BPS", device.address) + + client = device.connect(context, logger = logger) client.connectionState .filterNotNull() diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 53791efa..980873e5 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -50,6 +51,7 @@ import no.nordicsemi.android.kotlin.ble.profile.hrs.BodySensorLocationParser import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -67,6 +69,9 @@ internal class HRSService : NotificationService() { @Inject lateinit var repository: HRSRepository + @Inject + lateinit var stringConst: StringConst + private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -84,7 +89,9 @@ internal class HRSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@HRSService) + val logger = NordicLogger(this@HRSService, stringConst.APP_NAME, "CSC", device.address) + + client = device.connect(this@HRSService, logger = logger) client.connectionState .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index f4b37f4b..fb90a48f 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -49,6 +50,7 @@ import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.hts.HTSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -65,6 +67,9 @@ internal class HTSService : NotificationService() { @Inject lateinit var repository: HTSRepository + @Inject + lateinit var stringConst: StringConst + private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -82,7 +87,9 @@ internal class HTSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@HTSService) + val logger = NordicLogger(this@HTSService, stringConst.APP_NAME, "HTS", device.address) + + client = device.connect(this@HTSService, logger = logger) client.connectionState .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 7f95f71a..9404a9f1 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic @@ -62,6 +63,7 @@ import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattService import no.nordicsemi.android.kotlin.ble.server.main.service.BluetoothGattServerConnection import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -80,6 +82,9 @@ internal class PRXService : NotificationService() { @Inject lateinit var repository: PRXRepository + @Inject + lateinit var stringConst: StringConst + private lateinit var client: BleGattClient private lateinit var server: BleGattServer @@ -157,7 +162,9 @@ internal class PRXService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@PRXService, BleGattConnectOptions(autoConnect = true)) + val logger = NordicLogger(this@PRXService, stringConst.APP_NAME, "PRX", device.address) + + client = device.connect(this@PRXService, logger = logger) client.connectionStateWithStatus .filterNotNull() diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index b633cf96..647b474e 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -49,6 +50,7 @@ import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -65,6 +67,9 @@ internal class RSCSService : NotificationService() { @Inject lateinit var repository: RSCSRepository + @Inject + lateinit var stringConst: StringConst + private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -82,7 +87,9 @@ internal class RSCSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@RSCSService) + val logger = NordicLogger(this@RSCSService, stringConst.APP_NAME, "RSCS", device.address) + + client = device.connect(this@RSCSService, logger = logger) client.connectionState .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index f03fc378..d25feabe 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic @@ -51,6 +52,7 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -68,6 +70,9 @@ internal class UARTService : NotificationService() { @Inject lateinit var repository: UARTRepository + @Inject + lateinit var stringConst: StringConst + private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -85,7 +90,9 @@ internal class UARTService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@UARTService) + val logger = NordicLogger(this@UARTService, stringConst.APP_NAME, "UART", device.address) + + client = device.connect(this@UARTService, logger = logger) client.connectionState .onEach { repository.onConnectionStateChanged(it) } From c69b8c72a29d34c6cf17a44af018b4d2b66473cf Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 31 Mar 2023 15:42:36 +0200 Subject: [PATCH 033/101] Fix logger --- .../nrftoolbox/viewmodel/HomeViewModel.kt | 1 + .../android/service/OpenLoggerEvent.kt | 3 ++ .../android/bps/viewmodel/BPSViewModel.kt | 11 ++--- .../android/cgms/repository/CGMRepository.kt | 6 ++- .../android/cgms/repository/CGMService.kt | 8 +++- .../android/csc/repository/CSCRepository.kt | 11 ++--- .../android/csc/repository/CSCService.kt | 8 +++- .../gls/main/viewmodel/GLSViewModel.kt | 7 ++-- .../android/gls/repository/GLSRepository.kt | 42 ------------------- .../android/hrs/service/HRSRepository.kt | 10 +++-- .../android/hrs/service/HRSService.kt | 8 +++- .../android/hts/repository/HTSRepository.kt | 10 +++-- .../android/hts/repository/HTSService.kt | 8 +++- .../android/prx/repository/PRXRepository.kt | 6 ++- .../android/prx/repository/PRXService.kt | 9 ++-- .../android/rscs/repository/RSCSRepository.kt | 10 +++-- .../android/rscs/repository/RSCSService.kt | 8 +++- .../android/uart/repository/UARTRepository.kt | 11 ++--- .../android/uart/repository/UARTService.kt | 8 +++- 19 files changed, 99 insertions(+), 86 deletions(-) create mode 100644 lib_service/src/main/java/no/nordicsemi/android/service/OpenLoggerEvent.kt delete mode 100644 profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt index c156e5c0..3e817ba0 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.flow.onEach import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.ProfileOpenEvent import no.nordicsemi.android.cgms.repository.CGMRepository +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.navigation.DestinationId import no.nordicsemi.android.common.navigation.Navigator diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/OpenLoggerEvent.kt b/lib_service/src/main/java/no/nordicsemi/android/service/OpenLoggerEvent.kt new file mode 100644 index 00000000..52d8ed38 --- /dev/null +++ b/lib_service/src/main/java/no/nordicsemi/android/service/OpenLoggerEvent.kt @@ -0,0 +1,3 @@ +package no.nordicsemi.android.service + +class OpenLoggerEvent diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index a369ffef..b150a969 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -52,7 +52,7 @@ import no.nordicsemi.android.bps.view.BPSViewEvent import no.nordicsemi.android.bps.view.BPSViewState import no.nordicsemi.android.bps.view.DisconnectEvent import no.nordicsemi.android.bps.view.OpenLoggerEvent -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient @@ -61,10 +61,10 @@ import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser -import no.nordicsemi.android.kotlin.ble.profile.bps.data.BloodPressureMeasurementData import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementParser -import no.nordicsemi.android.kotlin.ble.profile.bps.data.IntermediateCuffPressureData import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureParser +import no.nordicsemi.android.kotlin.ble.profile.bps.data.BloodPressureMeasurementData +import no.nordicsemi.android.kotlin.ble.profile.bps.data.IntermediateCuffPressureData import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.ui.view.StringConst import java.util.* @@ -91,6 +91,7 @@ internal class BPSViewModel @Inject constructor( val state = _state.asStateFlow() private lateinit var client: BleGattClient + private lateinit var logger: NordicBlekLogger init { navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(BPS_SERVICE_UUID)) @@ -110,14 +111,14 @@ internal class BPSViewModel @Inject constructor( fun onEvent(event: BPSViewEvent) { when (event) { DisconnectEvent -> navigationManager.navigateUp() - OpenLoggerEvent -> TODO() + OpenLoggerEvent -> logger.launch() } } private fun startGattClient(device: ServerDevice) = viewModelScope.launch { _state.value = _state.value.copy(deviceName = device.name) - val logger = NordicLogger(context, stringConst.APP_NAME, "BPS", device.address) + logger = NordicBlekLogger(context, stringConst.APP_NAME, "BPS", device.address) client = device.connect(context, logger = logger) 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 19dfb9ae..7def0da5 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 @@ -45,6 +45,7 @@ import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus import no.nordicsemi.android.service.DisconnectAndStopEvent +import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject import javax.inject.Singleton @@ -64,6 +65,9 @@ class CGMRepository @Inject constructor( private val _command = simpleSharedFlow() internal val command = _command.asSharedFlow() + private val _loggerEvent = simpleSharedFlow() + internal val loggerEvent = _loggerEvent.asSharedFlow() + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } val hasRecords = data.value.records.isNotEmpty() val highestSequenceNumber = data.value.records.maxOfOrNull { it.sequenceNumber } ?: -1 @@ -94,7 +98,7 @@ class CGMRepository @Inject constructor( } fun openLogger() { - TODO() + _loggerEvent.tryEmit(OpenLoggerEvent()) } fun clear() { 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 5084fc93..0b826c1a 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 @@ -43,7 +43,7 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.ble.common.data.cgm.CGMSpecificOpsControlPointData import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber import no.nordicsemi.android.cgms.data.CGMServiceCommand -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic @@ -131,10 +131,14 @@ internal class CGMService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicLogger(this@CGMService, stringConst.APP_NAME, "CGM", device.address) + val logger = NordicBlekLogger(this@CGMService, stringConst.APP_NAME, "CGM", device.address) client = device.connect(this@CGMService, logger = logger) + repository.loggerEvent + .onEach { logger.launch() } + .launchIn(lifecycleScope) + client.connectionState .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index db1507b5..9a7b20c7 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -47,6 +47,7 @@ import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSize import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSizes import no.nordicsemi.android.service.DisconnectAndStopEvent +import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject import javax.inject.Singleton @@ -57,8 +58,6 @@ class CSCRepository @Inject constructor( private val context: Context, private val serviceManager: ServiceManager, ) { - private var logger: NordicLogger? = null - private val _wheelSize = MutableStateFlow(WheelSizes.default) internal val wheelSize = _wheelSize.asStateFlow() @@ -68,6 +67,9 @@ class CSCRepository @Inject constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() + private val _loggerEvent = simpleSharedFlow() + internal val loggerEvent = _loggerEvent.asSharedFlow() + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { @@ -99,11 +101,10 @@ class CSCRepository @Inject constructor( } fun openLogger() { - NordicLogger.launch(context, logger) + _loggerEvent.tryEmit(OpenLoggerEvent()) } fun release() { - logger = null _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 2f218f51..7d175df5 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -87,10 +87,14 @@ internal class CSCService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicLogger(this@CSCService, stringConst.APP_NAME, "CSC", device.address) + val logger = NordicBlekLogger(this@CSCService, stringConst.APP_NAME, "CSC", device.address) client = device.connect(this@CSCService, logger = logger) + repository.loggerEvent + .onEach { logger.launch() } + .launchIn(lifecycleScope) + client.connectionState .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 7ff5f277..93762a1c 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -48,7 +48,7 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.GlsDetailsDestinationId @@ -103,6 +103,7 @@ internal class GLSViewModel @Inject constructor( ) : ViewModel() { private lateinit var client: BleGattClient + private lateinit var logger: NordicBlekLogger private lateinit var glucoseMeasurementCharacteristic: BleGattCharacteristic private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic @@ -130,7 +131,7 @@ internal class GLSViewModel @Inject constructor( fun onEvent(event: GLSScreenViewEvent) { when (event) { - OpenLoggerEvent -> TODO() + OpenLoggerEvent -> logger.launch() DisconnectEvent -> navigationManager.navigateUp() is OnWorkingModeSelected -> onEvent(event) is OnGLSRecordClick -> navigateToDetails(event.record) @@ -156,7 +157,7 @@ internal class GLSViewModel @Inject constructor( } private fun startGattClient(device: ServerDevice) = viewModelScope.launch { - val logger = NordicLogger(context, stringConst.APP_NAME, "BPS", device.address) + logger = NordicBlekLogger(context, stringConst.APP_NAME, "BPS", device.address) client = device.connect(context, logger = logger) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt deleted file mode 100644 index c0c0ff6e..00000000 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/repository/GLSRepository.kt +++ /dev/null @@ -1,42 +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.gls.repository - -import dagger.hilt.android.scopes.ViewModelScoped -import javax.inject.Inject - -@ViewModelScoped -internal class GLSRepository @Inject constructor( - -) { - -} diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 829e3c9f..94cee9ae 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -38,12 +38,13 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData import no.nordicsemi.android.service.DisconnectAndStopEvent +import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject import javax.inject.Singleton @@ -54,7 +55,7 @@ class HRSRepository @Inject constructor( private val context: Context, private val serviceManager: ServiceManager ) { - private var logger: NordicLogger? = null + private var logger: NordicBlekLogger? = null private val _data = MutableStateFlow(HRSServiceData()) internal val data = _data.asStateFlow() @@ -62,6 +63,9 @@ class HRSRepository @Inject constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() + private val _loggerEvent = simpleSharedFlow() + internal val loggerEvent = _loggerEvent.asSharedFlow() + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { @@ -93,7 +97,7 @@ class HRSRepository @Inject constructor( } fun openLogger() { - NordicLogger.launch(context, logger) + _loggerEvent.tryEmit(OpenLoggerEvent()) } fun release() { diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 980873e5..b5c05088 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -89,10 +89,14 @@ internal class HRSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicLogger(this@HRSService, stringConst.APP_NAME, "CSC", device.address) + val logger = NordicBlekLogger(this@HRSService, stringConst.APP_NAME, "CSC", device.address) client = device.connect(this@HRSService, logger = logger) + repository.loggerEvent + .onEach { logger.launch() } + .launchIn(lifecycleScope) + client.connectionState .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index a0e921de..1cc7c7e8 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -38,13 +38,14 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.hts.view.TemperatureUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData import no.nordicsemi.android.service.DisconnectAndStopEvent +import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject import javax.inject.Singleton @@ -55,7 +56,7 @@ class HTSRepository @Inject constructor( private val context: Context, private val serviceManager: ServiceManager ) { - private var logger: NordicLogger? = null + private var logger: NordicBlekLogger? = null private val _data = MutableStateFlow(HTSServiceData()) internal val data = _data.asStateFlow() @@ -63,6 +64,9 @@ class HTSRepository @Inject constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() + private val _loggerEvent = simpleSharedFlow() + internal val loggerEvent = _loggerEvent.asSharedFlow() + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { @@ -90,7 +94,7 @@ class HTSRepository @Inject constructor( } fun openLogger() { - NordicLogger.launch(context, logger) + _loggerEvent.tryEmit(OpenLoggerEvent()) } fun release() { diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index fb90a48f..9e86d0a3 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -87,10 +87,14 @@ internal class HTSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicLogger(this@HTSService, stringConst.APP_NAME, "HTS", device.address) + val logger = NordicBlekLogger(this@HTSService, stringConst.APP_NAME, "HTS", device.address) client = device.connect(this@HTSService, logger = logger) + repository.loggerEvent + .onEach { logger.launch() } + .launchIn(lifecycleScope) + client.connectionState .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 4add8c10..755dbb03 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -44,6 +44,7 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import no.nordicsemi.android.prx.data.PRXServiceData import no.nordicsemi.android.service.DisconnectAndStopEvent +import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject import javax.inject.Singleton @@ -61,6 +62,9 @@ class PRXRepository @Inject internal constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() + private val _loggerEvent = simpleSharedFlow() + internal val loggerEvent = _loggerEvent.asSharedFlow() + private val _remoteAlarmLevel = simpleSharedFlow() internal val remoteAlarmLevel = _remoteAlarmLevel.asSharedFlow() @@ -99,7 +103,7 @@ class PRXRepository @Inject internal constructor( } fun openLogger() { - TODO() + _loggerEvent.tryEmit(OpenLoggerEvent()) } fun release() { diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 9404a9f1..93c79dde 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -40,13 +40,12 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectOptions import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty @@ -162,10 +161,14 @@ internal class PRXService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicLogger(this@PRXService, stringConst.APP_NAME, "PRX", device.address) + val logger = NordicBlekLogger(this@PRXService, stringConst.APP_NAME, "PRX", device.address) client = device.connect(this@PRXService, logger = logger) + repository.loggerEvent + .onEach { logger.launch() } + .launchIn(lifecycleScope) + client.connectionStateWithStatus .filterNotNull() .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 36b935fd..f1654fe8 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -38,12 +38,13 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.profile.rscs.data.RSCSData import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.service.DisconnectAndStopEvent +import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import javax.inject.Inject import javax.inject.Singleton @@ -54,7 +55,7 @@ class RSCSRepository @Inject constructor( private val context: Context, private val serviceManager: ServiceManager ) { - private var logger: NordicLogger? = null + private var logger: NordicBlekLogger? = null private val _data = MutableStateFlow(RSCSServiceData()) internal val data = _data.asStateFlow() @@ -62,6 +63,9 @@ class RSCSRepository @Inject constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() + private val _loggerEvent = simpleSharedFlow() + internal val loggerEvent = _loggerEvent.asSharedFlow() + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { @@ -85,7 +89,7 @@ class RSCSRepository @Inject constructor( } fun openLogger() { - NordicLogger.launch(context, logger) + _loggerEvent.tryEmit(OpenLoggerEvent()) } fun release() { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index 647b474e..e0874334 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -87,10 +87,14 @@ internal class RSCSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicLogger(this@RSCSService, stringConst.APP_NAME, "RSCS", device.address) + val logger = NordicBlekLogger(this@RSCSService, stringConst.APP_NAME, "RSCS", device.address) client = device.connect(this@RSCSService, logger = logger) + repository.loggerEvent + .onEach { logger.launch() } + .launchIn(lifecycleScope) + client.connectionState .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 19827880..0cc31462 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -38,10 +38,11 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.service.DisconnectAndStopEvent +import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.uart.data.ConfigurationDataSource import no.nordicsemi.android.uart.data.MacroEol @@ -60,8 +61,6 @@ class UARTRepository @Inject internal constructor( private val serviceManager: ServiceManager, private val configurationDataSource: ConfigurationDataSource ) { - private var logger: NordicLogger? = null - private val _data = MutableStateFlow(UARTServiceData()) internal val data = _data.asStateFlow() @@ -71,6 +70,9 @@ class UARTRepository @Inject internal constructor( private val _command = simpleSharedFlow() internal val command = _command.asSharedFlow() + private val _loggerEvent = simpleSharedFlow() + internal val loggerEvent = _loggerEvent.asSharedFlow() + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } val lastConfigurationName = configurationDataSource.lastConfigurationName @@ -115,7 +117,7 @@ class UARTRepository @Inject internal constructor( } fun openLogger() { - NordicLogger.launch(context, logger) + _loggerEvent.tryEmit(OpenLoggerEvent()) } suspend fun saveConfigurationName(name: String) { @@ -124,6 +126,5 @@ class UARTRepository @Inject internal constructor( fun release() { _stopEvent.tryEmit(DisconnectAndStopEvent()) - logger = null } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index d25feabe..ac0cec0b 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -40,7 +40,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic @@ -90,10 +90,14 @@ internal class UARTService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicLogger(this@UARTService, stringConst.APP_NAME, "UART", device.address) + val logger = NordicBlekLogger(this@UARTService, stringConst.APP_NAME, "UART", device.address) client = device.connect(this@UARTService, logger = logger) + repository.loggerEvent + .onEach { logger.launch() } + .launchIn(lifecycleScope) + client.connectionState .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() From 9271ee49552fd83537e8be0783cd6191cfaebcdc Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 3 Apr 2023 12:58:30 +0200 Subject: [PATCH 034/101] Improve logging --- .../android/nrftoolbox/viewmodel/HomeViewModel.kt | 1 - .../android/uart/repository/UARTRepository.kt | 1 - .../nordicsemi/android/uart/repository/UARTService.kt | 10 ++++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt index 3e817ba0..c156e5c0 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt @@ -43,7 +43,6 @@ import kotlinx.coroutines.flow.onEach import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.ProfileOpenEvent import no.nordicsemi.android.cgms.repository.CGMRepository -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.navigation.DestinationId import no.nordicsemi.android.common.navigation.Navigator diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 0cc31462..45a706e2 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -38,7 +38,6 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.service.DisconnectAndStopEvent diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index ac0cec0b..e3aeeeb5 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -33,6 +33,7 @@ package no.nordicsemi.android.uart.repository import android.annotation.SuppressLint import android.content.Intent +import android.util.Log import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.filterNotNull @@ -49,6 +50,7 @@ import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty import no.nordicsemi.android.kotlin.ble.core.data.BleWriteType import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.Mtu import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService @@ -106,11 +108,11 @@ internal class UARTService : NotificationService() { client.services .filterNotNull() - .onEach { configureGatt(it, device) } + .onEach { configureGatt(it, device, logger) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice, logger: NordicBlekLogger) { val uartService = services.findService(UART_SERVICE_UUID)!! val rxCharacteristic = uartService.findCharacteristic(UART_RX_CHARACTERISTIC_UUID)!! val txCharacteristic = uartService.findCharacteristic(UART_TX_CHARACTERISTIC_UUID)!! @@ -124,13 +126,17 @@ internal class UARTService : NotificationService() { txCharacteristic.getNotifications() .onEach { repository.onNewMessageReceived(String(it)) } + .onEach { logger.log(10, "Received: ${String(it)}") } .launchIn(lifecycleScope) repository.command .onEach { rxCharacteristic.splitWrite(it.toByteArray(), getWriteType(rxCharacteristic)) } .onEach { repository.onNewMessageSent(it) } + .onEach { logger.log(10, "Sent: $it") } .launchIn(lifecycleScope) + client.requestMtu(Mtu.max) + repository.onInitComplete(device) } From 1d9e3406210496e9e0ccee515ed019c7a88c685f Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 4 Apr 2023 13:22:30 +0200 Subject: [PATCH 035/101] Resize Activity on keyboard --- app/src/main/AndroidManifest.xml | 3 +-- settings.gradle.kts | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f51aa1b0..90e9fc2f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -54,8 +54,7 @@ android:name=".MainActivity" android:exported="true" android:label="@string/app_name" - android:launchMode="singleTask" - android:windowSoftInputMode="stateVisible|adjustResize" + android:windowSoftInputMode="adjustResize" android:theme="@style/NordicTheme.SplashScreen"> diff --git a/settings.gradle.kts b/settings.gradle.kts index 8383a4f7..12112e61 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -45,11 +45,12 @@ dependencyResolutionManagement { google() mavenCentral() gradlePluginPortal() + maven(url = "https://androidx.dev/storage/compose-compiler/repository/") maven(url = "https://jitpack.io") } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.3.3") + from("no.nordicsemi.android.gradle:version-catalog:1.3.6") } } } From d1f832abdc7df78841582cf5a7de8abbaa90d433 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 5 Apr 2023 12:20:48 +0200 Subject: [PATCH 036/101] Update version catalog --- settings.gradle.kts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 12112e61..6214ac70 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.3.6") + from("no.nordicsemi.android.gradle:version-catalog:1.4.1") } } } @@ -79,6 +79,6 @@ include(":lib_utils") // includeBuild("../Android-Common-Libraries") //} -if (file("../Kotlin-BLE-Library").exists()) { - includeBuild("../Kotlin-BLE-Library") -} +//if (file("../Kotlin-BLE-Library").exists()) { +// includeBuild("../Kotlin-BLE-Library") +//} From e1e48b4321e492b20db90880e351f620a159bb97 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 5 Apr 2023 12:46:20 +0200 Subject: [PATCH 037/101] Fix scrolling on UART --- .../no/nordicsemi/android/uart/view/OutputSection.kt | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/OutputSection.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/OutputSection.kt index a8fb21d2..95d4da59 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/OutputSection.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/OutputSection.kt @@ -53,8 +53,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -90,9 +88,6 @@ internal fun OutputSection(records: List, onEvent: (UARTViewEvent) - Spacer(modifier = Modifier.size(16.dp)) val scrollState = rememberLazyListState() - val scrollDown = remember { - derivedStateOf { scrollState.isScrolledToTheEnd() } - } LazyColumn( modifier = Modifier.fillMaxWidth(), @@ -114,8 +109,8 @@ internal fun OutputSection(records: List, onEvent: (UARTViewEvent) - } } - LaunchedEffect(records, scrollDown.value) { - if (!scrollDown.value || records.isEmpty()) { + LaunchedEffect(records) { + if (scrollState.isScrolledToTheEnd() || records.isEmpty()) { return@LaunchedEffect } launch { From 8283813dcbc964f840d0729dbc88be507ba7717a Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 5 Apr 2023 13:29:05 +0200 Subject: [PATCH 038/101] Add Blek source dependency --- .../nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt | 1 - settings.gradle.kts | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 93762a1c..0eda054c 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -132,7 +132,6 @@ internal class GLSViewModel @Inject constructor( fun onEvent(event: GLSScreenViewEvent) { when (event) { OpenLoggerEvent -> logger.launch() - DisconnectEvent -> navigationManager.navigateUp() is OnWorkingModeSelected -> onEvent(event) is OnGLSRecordClick -> navigateToDetails(event.record) DisconnectEvent -> navigationManager.navigateUp() diff --git a/settings.gradle.kts b/settings.gradle.kts index 6214ac70..0aaf51a1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -79,6 +79,6 @@ include(":lib_utils") // includeBuild("../Android-Common-Libraries") //} -//if (file("../Kotlin-BLE-Library").exists()) { -// includeBuild("../Kotlin-BLE-Library") -//} +if (file("../Kotlin-BLE-Library").exists()) { + includeBuild("../Kotlin-BLE-Library") +} From 0e0a2e0095790e636871b6b67d50f6148bdd0898 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 5 Apr 2023 14:37:52 +0200 Subject: [PATCH 039/101] Fix disapearing error message on gatt error --- .../nordicsemi/android/bps/view/BPSScreen.kt | 16 +++++------ .../nordicsemi/android/cgms/view/CGMScreen.kt | 16 +++++------ .../nordicsemi/android/csc/view/CSCScreen.kt | 16 +++++------ .../android/gls/main/view/GLSScreen.kt | 16 +++++------ .../gls/main/viewmodel/GLSViewModel.kt | 8 +----- .../nordicsemi/android/hrs/view/HRSScreen.kt | 16 +++++------ .../nordicsemi/android/hts/view/HTSScreen.kt | 16 +++++------ .../nordicsemi/android/prx/view/PRXScreen.kt | 16 +++++------ .../android/rscs/view/RSCSScreen.kt | 16 +++++------ .../android/uart/data/UARTServiceData.kt | 2 +- .../android/uart/view/UARTScreen.kt | 27 ++++++++++--------- 11 files changed, 72 insertions(+), 93 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 5f21341b..4239dad2 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -70,16 +70,14 @@ fun BPSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - if (state.deviceName == null) { - DeviceConnectingView() - } else { - when (state.result.connectionState) { - null, - GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> BPSContentView(state.result) { viewModel.onEvent(it) } + when (state.result.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> BPSContentView(state.result) { viewModel.onEvent(it) } } } } 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 590e908b..6c042100 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 @@ -71,16 +71,14 @@ fun CGMScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - if (state.deviceName == null) { - DeviceConnectingView() - } else { - 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) { viewModel.onEvent(it) } + 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) { viewModel.onEvent(it) } } } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 692ba72d..2c3bcbbc 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -71,16 +71,14 @@ fun CSCScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - if (state.deviceName == null) { - DeviceConnectingView() - } else { - when (state.connectionState) { - null, - GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> CSCContentView(state) { viewModel.onEvent(it) } + when (state.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> CSCContentView(state) { viewModel.onEvent(it) } } } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index 7637ad12..25c091cb 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -70,16 +70,14 @@ fun GLSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - if (state.deviceName == null) { - DeviceConnectingView() - } else { - when (state.glsServiceData.connectionState) { - null, - GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> GLSContentView(state.glsServiceData) { viewModel.onEvent(it) } + when (state.glsServiceData.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> GLSContentView(state.glsServiceData) { viewModel.onEvent(it) } } } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 0eda054c..3a35766a 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -34,6 +34,7 @@ package no.nordicsemi.android.gls.main.viewmodel import android.annotation.SuppressLint import android.content.Context import android.os.ParcelUuid +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -163,7 +164,6 @@ internal class GLSViewModel @Inject constructor( client.connectionState .filterNotNull() .onEach { _state.value = _state.value.copyWithNewConnectionState(it) } - .onEach { stopIfDisconnected(it) } .onEach { logAnalytics(it) } .launchIn(viewModelScope) @@ -209,12 +209,6 @@ internal class GLSViewModel @Inject constructor( _state.value = _state.value.copy(deviceName = device.name) //prevents UI from appearing before BLE connection is set up } - private fun stopIfDisconnected(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_DISCONNECTED) { - navigationManager.navigateUp() - } - } - private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) = viewModelScope.launch { when (data) { is NumberOfRecordsData -> onNumberOfRecordsReceived(data.numberOfRecords) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index 3a6b508e..f3669480 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -71,16 +71,14 @@ fun HRSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - if (state.deviceName == null) { - DeviceConnectingView() - } else { - when (state.connectionState) { - null, - GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> HRSContentView(state) { viewModel.onEvent(it) } + when (state.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> HRSContentView(state) { viewModel.onEvent(it) } } } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 1669179b..2d396cd9 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -71,16 +71,14 @@ fun HTSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - if (state.deviceName == null) { - DeviceConnectingView() - } else { - when (state.connectionState) { - null, - GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> HTSContentView(state) { viewModel.onEvent(it) } + when (state.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> HTSContentView(state) { viewModel.onEvent(it) } } } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index e8716a52..bf08e6c1 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -71,16 +71,14 @@ fun PRXScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - if (state.deviceName == null) { - DeviceConnectingView() - } else { - when (state.connectionState) { - null, - GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(getReason(state.isLinkLossDisconnected)) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> ContentView(state) { viewModel.onEvent(it) } + when (state.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(getReason(state.isLinkLossDisconnected)) { + NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> ContentView(state) { viewModel.onEvent(it) } } } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index 168233c1..643bd3cc 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -71,16 +71,14 @@ fun RSCSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - if (state.deviceName == null) { - DeviceConnectingView() - } else { - when (state.connectionState) { - null, - GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> RSCSContentView(state) { viewModel.onEvent(it) } + when (state.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> RSCSContentView(state) { viewModel.onEvent(it) } } } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt index 162b1307..ea24e58c 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt @@ -35,7 +35,7 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState internal data class UARTServiceData( val messages: List = emptyList(), - val connectionState: GattConnectionState = GattConnectionState.STATE_DISCONNECTED, + val connectionState: GattConnectionState? = null, val batteryLevel: Int? = null, val deviceName: String? = null ) { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index b7583647..9623917b 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -75,15 +75,14 @@ fun UARTScreen() { Column( modifier = Modifier.padding(it) ) { - if (state.uartManagerState.deviceName == null) { - PaddingBox { DeviceConnectingView() } - } else { - when (state.uartManagerState.connectionState) { - GattConnectionState.STATE_CONNECTING -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } - GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> PaddingBox { DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } } - GattConnectionState.STATE_CONNECTED -> SuccessScreen() + when (state.uartManagerState.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> PaddingBox { + DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } } + GattConnectionState.STATE_CONNECTED -> SuccessScreen() } } } @@ -112,12 +111,14 @@ private fun AppBar(state: UARTViewState, navigateUp: () -> Unit, viewModel: UART private fun SuccessScreen() { val input = stringResource(id = R.string.uart_input) val macros = stringResource(id = R.string.uart_macros) - val viewEntity = remember { PagerViewEntity( - listOf( - PagerViewItem(input) { KeyboardView() }, - PagerViewItem(macros) { MacroView() } + val viewEntity = remember { + PagerViewEntity( + listOf( + PagerViewItem(input) { KeyboardView() }, + PagerViewItem(macros) { MacroView() } + ) ) - ) } + } PagerView( viewEntity = viewEntity, modifier = Modifier.fillMaxSize(), From 60c6e03559bc942d93dc74fa1dd05e812c8b77dd Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 5 Apr 2023 15:38:17 +0200 Subject: [PATCH 040/101] Improve CGMS screen --- .../java/no/nordicsemi/android/cgms/view/CGMScreen.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 6c042100..48132d7f 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 @@ -51,6 +51,7 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar +import no.nordicsemi.android.ui.view.LoggerBackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -87,8 +88,12 @@ fun CGMScreen() { @Composable private fun AppBar(state: CGMServiceData, navigateUp: () -> Unit, viewModel: CGMViewModel) { if (state.deviceName?.isNotBlank() == true) { - LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { - viewModel.onEvent(OpenLoggerEvent) + if (state.connectionState == GattConnectionState.STATE_DISCONNECTING || state.connectionState == GattConnectionState.STATE_DISCONNECTED) { + LoggerBackIconAppBar(state.deviceName) { viewModel.onEvent(OpenLoggerEvent) } + } else { + LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { + viewModel.onEvent(OpenLoggerEvent) + } } } else { BackIconAppBar(stringResource(id = R.string.cgms_title), navigateUp) From 06ea417cb7d7904ac583677ad11d475060a65de9 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 5 Apr 2023 16:44:24 +0200 Subject: [PATCH 041/101] Add experimental connection error message --- .../nordicsemi/android/cgms/data/CGMServiceData.kt | 4 ++-- .../android/cgms/repository/CGMRepository.kt | 5 +++-- .../android/cgms/repository/CGMService.kt | 13 +++++++------ .../no/nordicsemi/android/cgms/view/CGMScreen.kt | 6 +++--- .../android/cgms/viewmodel/CGMViewModel.kt | 4 +--- .../nordicsemi/android/prx/data/PRXServiceData.kt | 3 ++- .../android/prx/repository/PRXRepository.kt | 7 ++++--- .../nordicsemi/android/prx/repository/PRXService.kt | 2 +- .../no/nordicsemi/android/prx/view/PRXScreen.kt | 4 ++-- .../android/prx/viewmodel/PRXViewModel.kt | 2 +- 10 files changed, 26 insertions(+), 24 deletions(-) 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 5aab70a1..2307c447 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 @@ -1,13 +1,13 @@ package no.nordicsemi.android.cgms.data -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMRecord import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus internal data class CGMServiceData( val records: List = emptyList(), val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null, + val connectionState: GattConnectionStateWithStatus? = null, val requestStatus: RequestStatus = RequestStatus.IDLE, val deviceName: String? = null ) 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 7def0da5..c5438c0e 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 @@ -43,6 +43,7 @@ import no.nordicsemi.android.cgms.data.CGMServiceData import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.OpenLoggerEvent @@ -68,7 +69,7 @@ class CGMRepository @Inject constructor( private val _loggerEvent = simpleSharedFlow() internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } + val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } val hasRecords = data.value.records.isNotEmpty() val highestSequenceNumber = data.value.records.maxOfOrNull { it.sequenceNumber } ?: -1 @@ -85,7 +86,7 @@ class CGMRepository @Inject constructor( _command.tryEmit(command) } - fun onConnectionStateChanged(connectionState: GattConnectionState?) { + fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { _data.value = _data.value.copy(connectionState = connectionState) } 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 0b826c1a..fd25f815 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 @@ -50,6 +50,7 @@ import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristi import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.cgm.CGMFeatureParser import no.nordicsemi.android.kotlin.ble.profile.cgm.CGMMeasurementParser @@ -139,7 +140,7 @@ internal class CGMService : NotificationService() { .onEach { logger.launch() } .launchIn(lifecycleScope) - client.connectionState + client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } @@ -187,10 +188,10 @@ internal class CGMService : NotificationService() { .mapNotNull { CGMSpecificOpsControlPointParser.parse(it) } .onEach { if (it.isOperationCompleted) { - if (it.requestCode == CGMOpCode.CGM_OP_CODE_START_SESSION) { - sessionStartTime = System.currentTimeMillis() + sessionStartTime = if (it.requestCode == CGMOpCode.CGM_OP_CODE_START_SESSION) { + System.currentTimeMillis() } else { - sessionStartTime = 0 + 0 } } else { if (it.requestCode == CGMOpCode.CGM_OP_CODE_START_SESSION && it.errorCode == CGMErrorCode.CGM_ERROR_PROCEDURE_NOT_COMPLETED) { @@ -298,8 +299,8 @@ internal class CGMService : NotificationService() { recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) } - private fun stopIfDisconnected(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { stopSelf() } } 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 48132d7f..25edd66e 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 @@ -72,11 +72,11 @@ fun CGMScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> CGMContentView(state) { viewModel.onEvent(it) } @@ -88,7 +88,7 @@ fun CGMScreen() { @Composable private fun AppBar(state: CGMServiceData, navigateUp: () -> Unit, viewModel: CGMViewModel) { if (state.deviceName?.isNotBlank() == true) { - if (state.connectionState == GattConnectionState.STATE_DISCONNECTING || state.connectionState == GattConnectionState.STATE_DISCONNECTED) { + if (state.connectionState?.state == GattConnectionState.STATE_DISCONNECTING || state.connectionState?.state == GattConnectionState.STATE_DISCONNECTED) { LoggerBackIconAppBar(state.deviceName) { viewModel.onEvent(OpenLoggerEvent) } } else { LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { 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 96bbef5c..abe520d8 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 @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -76,7 +74,7 @@ internal class CGMViewModel @Inject constructor( } repository.data.onEach { - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { + if (it.connectionState?.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.CGMS)) } }.launchIn(viewModelScope) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt index 36563a50..48e2a373 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -2,13 +2,14 @@ package no.nordicsemi.android.prx.data import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel data class PRXServiceData( val localAlarmLevel: AlarmLevel = AlarmLevel.NONE, val linkLossAlarmLevel: AlarmLevel = AlarmLevel.HIGH, val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null, + val connectionState: GattConnectionStateWithStatus? = null, val connectionStatus: BleGattConnectionStatus? = null, val isRemoteAlarm: Boolean = false, val deviceName: String? = null diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 755dbb03..13d72584 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -41,6 +41,7 @@ import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import no.nordicsemi.android.prx.data.PRXServiceData import no.nordicsemi.android.service.DisconnectAndStopEvent @@ -68,7 +69,7 @@ class PRXRepository @Inject internal constructor( private val _remoteAlarmLevel = simpleSharedFlow() internal val remoteAlarmLevel = _remoteAlarmLevel.asSharedFlow() - val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } + val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(PRXService::class.java, device) @@ -78,8 +79,8 @@ class PRXRepository @Inject internal constructor( _data.value = _data.value.copy(deviceName = device.name) } - fun onConnectionStateChanged(connection: Pair) { - _data.value = _data.value.copy(connectionState = connection.first, connectionStatus = connection.second) + fun onConnectionStateChanged(connection: GattConnectionStateWithStatus) { + _data.value = _data.value.copy(connectionState = connection) } fun setLocalAlarmLevel(alarmLevel: AlarmLevel) { diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 93c79dde..9cef5629 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -173,7 +173,7 @@ internal class PRXService : NotificationService() { .filterNotNull() .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() - .onEach { stopIfDisconnected(it.first, it.second) } + .onEach { stopIfDisconnected(it.state, it.status) } .launchIn(lifecycleScope) client.services diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index bf08e6c1..206d0297 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -71,11 +71,11 @@ fun PRXScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(getReason(state.isLinkLossDisconnected)) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> ContentView(state) { viewModel.onEvent(it) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index b56e7849..c2a30280 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -83,7 +83,7 @@ internal class PRXViewModel @Inject constructor( alarmHandler.playAlarm(it.linkLossAlarmLevel) } - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { + if (it.connectionState?.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.PRX)) } }.launchIn(viewModelScope) From d0aeefb0105866976d906dc80de801fc8aa6ecd2 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 18 Apr 2023 12:52:18 +0200 Subject: [PATCH 042/101] Fix toolbar title --- lib_ui/build.gradle.kts | 1 + .../nordicsemi/android/ui/view/TopAppBar.kt | 24 ++++++++++++ .../android/bps/data/BPSServiceData.kt | 4 +- .../nordicsemi/android/bps/view/BPSScreen.kt | 30 ++++++--------- .../android/bps/viewmodel/BPSViewModel.kt | 9 +++-- .../android/cgms/repository/CGMRepository.kt | 2 + .../nordicsemi/android/cgms/view/CGMScreen.kt | 30 +++++++-------- .../android/csc/data/CSCServiceData.kt | 4 +- .../android/csc/repository/CSCRepository.kt | 5 ++- .../android/csc/repository/CSCService.kt | 7 ++-- .../nordicsemi/android/csc/view/CSCScreen.kt | 29 ++++++-------- .../android/csc/viewmodel/CSCViewModel.kt | 2 +- .../android/gls/data/GLSServiceData.kt | 3 +- .../android/gls/main/view/GLSScreen.kt | 30 ++++++--------- .../android/gls/main/view/GLSViewState.kt | 3 +- .../gls/main/viewmodel/GLSViewModel.kt | 7 ++-- .../android/hrs/data/HRSServiceData.kt | 3 +- .../android/hrs/service/HRSRepository.kt | 5 ++- .../android/hrs/service/HRSService.kt | 7 ++-- .../nordicsemi/android/hrs/view/HRSScreen.kt | 29 ++++++-------- .../android/hrs/viewmodel/HRSViewModel.kt | 2 +- .../android/hts/data/HTSServiceData.kt | 3 +- .../android/hts/repository/HTSRepository.kt | 5 ++- .../android/hts/repository/HTSService.kt | 7 ++-- .../nordicsemi/android/hts/view/HTSScreen.kt | 25 ++++++------ .../android/hts/viewmodel/HTSViewModel.kt | 2 +- .../android/prx/repository/PRXRepository.kt | 1 - .../nordicsemi/android/prx/view/PRXScreen.kt | 38 +++++++------------ .../android/prx/viewmodel/PRXViewModel.kt | 2 +- .../android/rscs/data/RSCSServiceData.kt | 3 +- .../android/rscs/repository/RSCSRepository.kt | 5 ++- .../android/rscs/repository/RSCSService.kt | 7 ++-- .../android/rscs/view/RSCSScreen.kt | 29 ++++++-------- .../android/rscs/viewmodel/RSCSViewModel.kt | 2 +- .../android/uart/data/UARTServiceData.kt | 3 +- .../android/uart/repository/UARTRepository.kt | 5 ++- .../android/uart/repository/UARTService.kt | 7 ++-- .../android/uart/view/UARTScreen.kt | 33 ++++++---------- .../android/uart/viewmodel/UARTViewModel.kt | 2 +- 39 files changed, 203 insertions(+), 212 deletions(-) diff --git a/lib_ui/build.gradle.kts b/lib_ui/build.gradle.kts index 86970f79..7fb83133 100644 --- a/lib_ui/build.gradle.kts +++ b/lib_ui/build.gradle.kts @@ -46,4 +46,5 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) implementation(libs.androidx.activity.compose) + implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") } diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/TopAppBar.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/TopAppBar.kt index b89a039e..95b3974c 100644 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/TopAppBar.kt +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/TopAppBar.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.ui.view +import androidx.annotation.StringRes import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack @@ -43,6 +44,8 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.ui.R @OptIn(ExperimentalMaterial3Api::class) @@ -168,3 +171,24 @@ fun LoggerIconAppBar(text: String, onClick: () -> Unit, onDisconnectClick: () -> } ) } + +@Composable +fun ProfileAppBar( + deviceName: String?, + connectionState: GattConnectionStateWithStatus?, + @StringRes + title: Int, + navigateUp: () -> Unit, + disconnect: () -> Unit, + openLogger: () -> Unit +) { + if (deviceName?.isNotBlank() == true) { + if (connectionState?.state == GattConnectionState.STATE_DISCONNECTING || connectionState?.state == GattConnectionState.STATE_DISCONNECTED) { + LoggerBackIconAppBar(deviceName, openLogger) + } else { + LoggerIconAppBar(deviceName, navigateUp, disconnect, openLogger) + } + } else { + BackIconAppBar(stringResource(id = title), navigateUp) + } +} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt index b1608080..4c254197 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSServiceData.kt @@ -1,6 +1,6 @@ package no.nordicsemi.android.bps.data -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.bps.data.BloodPressureMeasurementData import no.nordicsemi.android.kotlin.ble.profile.bps.data.IntermediateCuffPressureData @@ -8,5 +8,5 @@ data class BPSServiceData ( val bloodPressureMeasurement: BloodPressureMeasurementData? = null, val intermediateCuffPressure: IntermediateCuffPressureData? = null, val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null + val connectionState: GattConnectionStateWithStatus? = null ) \ No newline at end of file diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 4239dad2..1218c4cf 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -40,7 +40,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.bps.R @@ -49,9 +48,8 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton +import no.nordicsemi.android.ui.view.ProfileAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -62,7 +60,16 @@ fun BPSScreen() { val navigateUp = { viewModel.onEvent(DisconnectEvent) } Scaffold( - topBar = { AppBar(state = state, navigateUp = navigateUp, viewModel = viewModel) } + topBar = { + ProfileAppBar( + deviceName = state.deviceName, + connectionState = state.result.connectionState, + title = R.string.bps_title, + navigateUp = navigateUp, + disconnect = { viewModel.onEvent(DisconnectEvent) }, + openLogger = { viewModel.onEvent(OpenLoggerEvent) } + ) + } ) { Column( modifier = Modifier @@ -70,7 +77,7 @@ fun BPSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.result.connectionState) { + when (state.result.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, @@ -82,16 +89,3 @@ fun BPSScreen() { } } } - -@Composable -private fun AppBar(state: BPSViewState, navigateUp: () -> Unit, viewModel: BPSViewModel) { - if (state.deviceName == null) { - BackIconAppBar(stringResource(id = R.string.bps_title), navigateUp) - } else { - LoggerIconAppBar(state.deviceName, { - viewModel.onEvent(DisconnectEvent) - }, { viewModel.onEvent(DisconnectEvent) }) { - viewModel.onEvent(OpenLoggerEvent) - } - } -} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index b150a969..bcb7e1e9 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -60,6 +60,7 @@ import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementParser import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureParser @@ -122,11 +123,11 @@ internal class BPSViewModel @Inject constructor( client = device.connect(context, logger = logger) - client.connectionState + client.connectionStateWithStatus .filterNotNull() .onEach { onDataUpdate(it) } - .onEach { stopIfDisconnected(it) } - .onEach { logAnalytics(it) } + .onEach { stopIfDisconnected(it.state) } + .onEach { logAnalytics(it.state) } .launchIn(viewModelScope) client.services @@ -158,7 +159,7 @@ internal class BPSViewModel @Inject constructor( ?.launchIn(viewModelScope) } - private fun onDataUpdate(connectionState: GattConnectionState) { + private fun onDataUpdate(connectionState: GattConnectionStateWithStatus) { val newResult = _state.value.result.copy(connectionState = connectionState) _state.value = _state.value.copy(result = newResult) } 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 c5438c0e..713a1bf5 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 @@ -32,6 +32,7 @@ package no.nordicsemi.android.cgms.repository import android.content.Context +import android.util.Log import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -87,6 +88,7 @@ class CGMRepository @Inject constructor( } fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { + Log.i("AAATESTAAA", "Connection state: $connectionState") _data.value = _data.value.copy(connectionState = connectionState) } 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 25edd66e..8555c10c 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 @@ -48,12 +48,12 @@ 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 -import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerBackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton +import no.nordicsemi.android.ui.view.ProfileAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -64,7 +64,16 @@ fun CGMScreen() { val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( - topBar = { AppBar(state = state, navigateUp = navigateUp, viewModel = viewModel) } + topBar = { + ProfileAppBar( + deviceName = state.deviceName, + connectionState = state.connectionState, + title = R.string.cgms_title, + navigateUp = navigateUp, + disconnect = { viewModel.onEvent(DisconnectEvent) }, + openLogger = { viewModel.onEvent(OpenLoggerEvent) } + ) + } ) { Column( modifier = Modifier @@ -75,27 +84,14 @@ fun CGMScreen() { when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> CGMContentView(state) { viewModel.onEvent(it) } } } } } - -@Composable -private fun AppBar(state: CGMServiceData, navigateUp: () -> Unit, viewModel: CGMViewModel) { - if (state.deviceName?.isNotBlank() == true) { - if (state.connectionState?.state == GattConnectionState.STATE_DISCONNECTING || state.connectionState?.state == GattConnectionState.STATE_DISCONNECTED) { - LoggerBackIconAppBar(state.deviceName) { viewModel.onEvent(OpenLoggerEvent) } - } else { - LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { - viewModel.onEvent(OpenLoggerEvent) - } - } - } else { - BackIconAppBar(stringResource(id = R.string.cgms_title), navigateUp) - } -} diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt index 635fb318..917c90df 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt @@ -1,12 +1,12 @@ package no.nordicsemi.android.csc.data -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData internal data class CSCServiceData( val data: CSCData = CSCData(), val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null, + val connectionState: GattConnectionStateWithStatus? = null, val speedUnit: SpeedUnit = SpeedUnit.M_S, val deviceName: String? = null ) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 9a7b20c7..8b1b36ac 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -43,6 +43,7 @@ import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSize import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSizes @@ -70,7 +71,7 @@ class CSCRepository @Inject constructor( private val _loggerEvent = simpleSharedFlow() internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } + val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(CSCService::class.java, device) @@ -88,7 +89,7 @@ class CSCRepository @Inject constructor( _wheelSize.value = wheelSize } - fun onConnectionStateChanged(connectionState: GattConnectionState?) { + fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { _data.value = _data.value.copy(connectionState = connectionState) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 7d175df5..fc357958 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -46,6 +46,7 @@ import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.csc.CSCDataParser import no.nordicsemi.android.service.DEVICE_DATA @@ -95,7 +96,7 @@ internal class CSCService : NotificationService() { .onEach { logger.launch() } .launchIn(lifecycleScope) - client.connectionState + client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } @@ -127,8 +128,8 @@ internal class CSCService : NotificationService() { repository.onInitComplete(device) } - private fun stopIfDisconnected(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { stopSelf() } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 2c3bcbbc..47a8a668 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -40,19 +40,16 @@ import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.csc.R -import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.viewmodel.CSCViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton +import no.nordicsemi.android.ui.view.ProfileAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -63,7 +60,16 @@ fun CSCScreen() { val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( - topBar = { AppBar(state, navigateUp, viewModel) } + topBar = { + ProfileAppBar( + deviceName = state.deviceName, + connectionState = state.connectionState, + title = R.string.csc_title, + navigateUp = navigateUp, + disconnect = { viewModel.onEvent(OnDisconnectButtonClick) }, + openLogger = { viewModel.onEvent(OpenLogger) } + ) + } ) { Column( modifier = Modifier @@ -71,7 +77,7 @@ fun CSCScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, @@ -83,14 +89,3 @@ fun CSCScreen() { } } } - -@Composable -private fun AppBar(state: CSCServiceData, navigateUp: () -> Unit, viewModel: CSCViewModel) { - if (state.deviceName?.isNotBlank() == true) { - LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(OnDisconnectButtonClick) }) { - viewModel.onEvent(OpenLogger) - } - } else { - BackIconAppBar(stringResource(id = R.string.csc_title), navigateUp) - } -} diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index 19fb8c6c..47e61ed5 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -75,7 +75,7 @@ internal class CSCViewModel @Inject constructor( } repository.data.onEach { - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { + if (it.connectionState?.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.CSC)) } }.launchIn(viewModelScope) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt index f5a5cb0a..a5077951 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt @@ -32,6 +32,7 @@ package no.nordicsemi.android.gls.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus @@ -39,6 +40,6 @@ import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus internal data class GLSServiceData( val records: Map = mapOf(), val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null, + val connectionState: GattConnectionStateWithStatus? = null, val requestStatus: RequestStatus = RequestStatus.IDLE ) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index 25c091cb..1aeece97 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -40,7 +40,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView @@ -49,9 +48,8 @@ import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.gls.R import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton +import no.nordicsemi.android.ui.view.ProfileAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -62,7 +60,16 @@ fun GLSScreen() { val navigateUp = { viewModel.onEvent(DisconnectEvent) } Scaffold( - topBar = { AppBar(state, navigateUp, viewModel) } + topBar = { + ProfileAppBar( + deviceName = state.deviceName, + connectionState = state.glsServiceData.connectionState, + title = R.string.gls_title, + navigateUp = navigateUp, + disconnect = { viewModel.onEvent(DisconnectEvent) }, + openLogger = { viewModel.onEvent(OpenLoggerEvent) } + ) + } ) { Column( modifier = Modifier @@ -70,7 +77,7 @@ fun GLSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.glsServiceData.connectionState) { + when (state.glsServiceData.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, @@ -82,16 +89,3 @@ fun GLSScreen() { } } } - -@Composable -private fun AppBar(state: GLSViewState, navigateUp: () -> Unit, viewModel: GLSViewModel) { - if (state.deviceName == null) { - BackIconAppBar(stringResource(id = R.string.gls_title), navigateUp) - } else { - LoggerIconAppBar(state.deviceName, { - viewModel.onEvent(DisconnectEvent) - }, { viewModel.onEvent(DisconnectEvent) }) { - viewModel.onEvent(OpenLoggerEvent) - } - } -} diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt index b77e792c..2f04e457 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt @@ -33,6 +33,7 @@ package no.nordicsemi.android.gls.main.view import no.nordicsemi.android.gls.data.GLSServiceData import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus @@ -42,7 +43,7 @@ internal data class GLSViewState( val deviceName: String? = null ) { - fun copyWithNewConnectionState(connectionState: GattConnectionState): GLSViewState { + fun copyWithNewConnectionState(connectionState: GattConnectionStateWithStatus): GLSViewState { return copy(glsServiceData = glsServiceData.copy(connectionState = connectionState)) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 3a35766a..03fd40e3 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -66,6 +66,7 @@ import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristi import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementContextParser import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser @@ -161,7 +162,7 @@ internal class GLSViewModel @Inject constructor( client = device.connect(context, logger = logger) - client.connectionState + client.connectionStateWithStatus .filterNotNull() .onEach { _state.value = _state.value.copyWithNewConnectionState(it) } .onEach { logAnalytics(it) } @@ -173,8 +174,8 @@ internal class GLSViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun logAnalytics(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_CONNECTED) { + private fun logAnalytics(connectionState: GattConnectionStateWithStatus) { + if (connectionState.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.GLS)) } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt index 81a23ac0..89f5cebf 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt @@ -32,13 +32,14 @@ package no.nordicsemi.android.hrs.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData internal data class HRSServiceData( val data: List = emptyList(), val bodySensorLocation: Int? = null, val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null, + val connectionState: GattConnectionStateWithStatus? = null, val zoomIn: Boolean = false, val deviceName: String? = null ) { diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 94cee9ae..47d84a69 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -42,6 +42,7 @@ import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.OpenLoggerEvent @@ -66,7 +67,7 @@ class HRSRepository @Inject constructor( private val _loggerEvent = simpleSharedFlow() internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } + val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(HRSService::class.java, device) @@ -80,7 +81,7 @@ class HRSRepository @Inject constructor( _data.value = _data.value.copy(zoomIn = !_data.value.zoomIn) } - fun onConnectionStateChanged(connectionState: GattConnectionState?) { + fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { _data.value = _data.value.copy(connectionState = connectionState) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index b5c05088..5248a3c4 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -46,6 +46,7 @@ import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.hrs.BodySensorLocationParser import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSDataParser @@ -97,7 +98,7 @@ internal class HRSService : NotificationService() { .onEach { logger.launch() } .launchIn(lifecycleScope) - client.connectionState + client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } @@ -132,8 +133,8 @@ internal class HRSService : NotificationService() { repository.onInitComplete(device) } - private fun stopIfDisconnected(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { stopSelf() } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index f3669480..36394e06 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -40,19 +40,16 @@ import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.hrs.R -import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.hrs.viewmodel.HRSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton +import no.nordicsemi.android.ui.view.ProfileAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -63,7 +60,16 @@ fun HRSScreen() { val navigateUp = { viewModel.onEvent(NavigateUpEvent) } Scaffold( - topBar = { AppBar(state, navigateUp, viewModel) } + topBar = { + ProfileAppBar( + deviceName = state.deviceName, + connectionState = state.connectionState, + title = R.string.hrs_title, + navigateUp = navigateUp, + disconnect = { viewModel.onEvent(DisconnectEvent) }, + openLogger = { viewModel.onEvent(OpenLoggerEvent) } + ) + } ) { Column( modifier = Modifier @@ -71,7 +77,7 @@ fun HRSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, @@ -83,14 +89,3 @@ fun HRSScreen() { } } } - -@Composable -private fun AppBar(state: HRSServiceData, navigateUp: () -> Unit, viewModel: HRSViewModel) { - if (state.deviceName?.isNotBlank() == true) { - LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { - viewModel.onEvent(OpenLoggerEvent) - } - } else { - BackIconAppBar(stringResource(id = R.string.hrs_title), navigateUp) - } -} diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index 5de6a338..3168334b 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -73,7 +73,7 @@ internal class HRSViewModel @Inject constructor( } repository.data.onEach { - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { + if (it.connectionState?.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.HRS)) } }.launchIn(viewModelScope) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt index 210c4b8c..750fd306 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt @@ -33,12 +33,13 @@ package no.nordicsemi.android.hts.data import no.nordicsemi.android.hts.view.TemperatureUnit import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData internal data class HTSServiceData( val data: HTSData = HTSData(), val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null, + val connectionState: GattConnectionStateWithStatus? = null, val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS, val deviceName: String? = null ) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 1cc7c7e8..89322d7d 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -43,6 +43,7 @@ import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.hts.view.TemperatureUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.OpenLoggerEvent @@ -67,7 +68,7 @@ class HTSRepository @Inject constructor( private val _loggerEvent = simpleSharedFlow() internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } + val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(HTSService::class.java, device) @@ -81,7 +82,7 @@ class HTSRepository @Inject constructor( _data.value = _data.value.copy(temperatureUnit = temperatureUnit) } - fun onConnectionStateChanged(connectionState: GattConnectionState?) { + fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { _data.value = _data.value.copy(connectionState = connectionState) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 9e86d0a3..3fe6d797 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -46,6 +46,7 @@ import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.hts.HTSDataParser import no.nordicsemi.android.service.DEVICE_DATA @@ -95,7 +96,7 @@ internal class HTSService : NotificationService() { .onEach { logger.launch() } .launchIn(lifecycleScope) - client.connectionState + client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } @@ -126,8 +127,8 @@ internal class HTSService : NotificationService() { repository.onInitComplete(device) } - private fun stopIfDisconnected(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { stopSelf() } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 2d396cd9..3d320130 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -53,6 +53,7 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton +import no.nordicsemi.android.ui.view.ProfileAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -63,7 +64,16 @@ fun HTSScreen() { val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( - topBar = { AppBar(state, navigateUp, viewModel) } + topBar = { + ProfileAppBar( + deviceName = state.deviceName, + connectionState = state.connectionState, + title = R.string.hts_title, + navigateUp = navigateUp, + disconnect = { viewModel.onEvent(DisconnectEvent) }, + openLogger = { viewModel.onEvent(OpenLoggerEvent) } + ) + } ) { Column( modifier = Modifier @@ -71,7 +81,7 @@ fun HTSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, @@ -83,14 +93,3 @@ fun HTSScreen() { } } } - -@Composable -private fun AppBar(state: HTSServiceData, navigateUp: () -> Unit, viewModel: HTSViewModel) { - if (state.deviceName?.isNotBlank() == true) { - LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { - viewModel.onEvent(OpenLoggerEvent) - } - } else { - BackIconAppBar(stringResource(id = R.string.hts_title), navigateUp) - } -} diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index e9d516ad..8d28b961 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -73,7 +73,7 @@ internal class HTSViewModel @Inject constructor( } repository.data.onEach { - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { + if (it.connectionState?.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.HTS)) } }.launchIn(viewModelScope) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 13d72584..183a53af 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -39,7 +39,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index 206d0297..23cc17f7 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.prx.view +import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -40,19 +41,15 @@ import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView -import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.prx.R -import no.nordicsemi.android.prx.data.PRXServiceData import no.nordicsemi.android.prx.viewmodel.PRXViewModel -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton +import no.nordicsemi.android.ui.view.ProfileAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -63,7 +60,16 @@ fun PRXScreen() { val navigateUp = { viewModel.onEvent(NavigateUpEvent) } Scaffold( - topBar = { AppBar(state, navigateUp, viewModel) } + topBar = { + ProfileAppBar( + deviceName = state.deviceName, + connectionState = state.connectionState, + title = R.string.prx_title, + navigateUp = navigateUp, + disconnect = { viewModel.onEvent(DisconnectEvent) }, + openLogger = { viewModel.onEvent(OpenLoggerEvent) } + ) + } ) { Column( modifier = Modifier @@ -71,6 +77,7 @@ fun PRXScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { + Log.i("AAATESTAAA", "State: ${state.connectionState?.state}") when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } @@ -83,22 +90,3 @@ fun PRXScreen() { } } } - -private fun getReason(isLinkLoss: Boolean): Reason { - return if (isLinkLoss) { - Reason.LINK_LOSS - } else { - Reason.UNKNOWN - } -} - -@Composable -private fun AppBar(state: PRXServiceData, navigateUp: () -> Unit, viewModel: PRXViewModel) { - if (state.deviceName?.isNotBlank() == true) { - LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { - viewModel.onEvent(OpenLoggerEvent) - } - } else { - BackIconAppBar(stringResource(id = R.string.prx_title), navigateUp) - } -} diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index c2a30280..53e86ce7 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -121,9 +121,9 @@ internal class PRXViewModel @Inject constructor( } private fun disconnect() { - repository.release() alarmHandler.pauseAlarm() navigationManager.navigateUp() + repository.release() } override fun onCleared() { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt index f98884f8..5ffb23b6 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt @@ -34,13 +34,14 @@ package no.nordicsemi.android.rscs.data import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.rscs.data.RSCSData import no.nordicsemi.android.rscs.R internal data class RSCSServiceData( val data: RSCSData = RSCSData(), val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null, + val connectionState: GattConnectionStateWithStatus? = null, val deviceName: String? = null ) { @Composable diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index f1654fe8..1d1950d0 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -41,6 +41,7 @@ import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.rscs.data.RSCSData import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.service.DisconnectAndStopEvent @@ -66,7 +67,7 @@ class RSCSRepository @Inject constructor( private val _loggerEvent = simpleSharedFlow() internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } + val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(RSCSService::class.java, device) @@ -76,7 +77,7 @@ class RSCSRepository @Inject constructor( _data.value = _data.value.copy(deviceName = device.name) } - fun onConnectionStateChanged(connectionState: GattConnectionState?) { + fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { _data.value = _data.value.copy(connectionState = connectionState) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index e0874334..a3cc54cd 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -46,6 +46,7 @@ import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSDataParser import no.nordicsemi.android.service.DEVICE_DATA @@ -95,7 +96,7 @@ internal class RSCSService : NotificationService() { .onEach { logger.launch() } .launchIn(lifecycleScope) - client.connectionState + client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } @@ -126,8 +127,8 @@ internal class RSCSService : NotificationService() { repository.onInitComplete(device) } - private fun stopIfDisconnected(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { stopSelf() } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index 643bd3cc..dd4d4983 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -40,7 +40,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView @@ -48,11 +47,9 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.rscs.R -import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.rscs.viewmodel.RSCSViewModel -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton +import no.nordicsemi.android.ui.view.ProfileAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -63,7 +60,16 @@ fun RSCSScreen() { val navigateUp = { viewModel.onEvent(NavigateUpEvent) } Scaffold( - topBar = { AppBar(state, navigateUp, viewModel) } + topBar = { + ProfileAppBar( + deviceName = state.deviceName, + connectionState = state.connectionState, + title = R.string.rscs_title, + navigateUp = navigateUp, + disconnect = { viewModel.onEvent(DisconnectEvent) }, + openLogger = { viewModel.onEvent(OpenLoggerEvent) } + ) + } ) { Column( modifier = Modifier @@ -71,7 +77,7 @@ fun RSCSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, @@ -83,14 +89,3 @@ fun RSCSScreen() { } } } - -@Composable -private fun AppBar(state: RSCSServiceData, navigateUp: () -> Unit, viewModel: RSCSViewModel) { - if (state.deviceName?.isNotBlank() == true) { - LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { - viewModel.onEvent(OpenLoggerEvent) - } - } else { - BackIconAppBar(stringResource(id = R.string.rscs_title), navigateUp) - } -} diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 50d6d80a..57d912ac 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -72,7 +72,7 @@ internal class RSCSViewModel @Inject constructor( } repository.data.onEach { - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { + if (it.connectionState?.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.RSCS)) } }.launchIn(viewModelScope) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt index ea24e58c..51df55e0 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt @@ -32,10 +32,11 @@ package no.nordicsemi.android.uart.data import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus internal data class UARTServiceData( val messages: List = emptyList(), - val connectionState: GattConnectionState? = null, + val connectionState: GattConnectionStateWithStatus? = null, val batteryLevel: Int? = null, val deviceName: String? = null ) { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 45a706e2..e59d2c79 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager @@ -72,7 +73,7 @@ class UARTRepository @Inject internal constructor( private val _loggerEvent = simpleSharedFlow() internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } + val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } val lastConfigurationName = configurationDataSource.lastConfigurationName @@ -80,7 +81,7 @@ class UARTRepository @Inject internal constructor( serviceManager.startService(UARTService::class.java, device) } - fun onConnectionStateChanged(connectionState: GattConnectionState) { + fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { _data.value = _data.value.copy(connectionState = connectionState) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index e3aeeeb5..44303127 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -50,6 +50,7 @@ import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty import no.nordicsemi.android.kotlin.ble.core.data.BleWriteType import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.core.data.Mtu import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.service.DEVICE_DATA @@ -100,7 +101,7 @@ internal class UARTService : NotificationService() { .onEach { logger.launch() } .launchIn(lifecycleScope) - client.connectionState + client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } @@ -148,8 +149,8 @@ internal class UARTService : NotificationService() { } } - private fun stopIfDisconnected(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { stopSelf() } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index 9623917b..aab035c0 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -60,6 +60,7 @@ import no.nordicsemi.android.uart.viewmodel.UARTViewModel import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton +import no.nordicsemi.android.ui.view.ProfileAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -70,12 +71,21 @@ fun UARTScreen() { val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( - topBar = { AppBar(state, navigateUp, viewModel) } + topBar = { + ProfileAppBar( + deviceName = state.uartManagerState.deviceName, + connectionState = state.uartManagerState.connectionState, + title = R.string.uart_title, + navigateUp = navigateUp, + disconnect = { viewModel.onEvent(DisconnectEvent) }, + openLogger = { viewModel.onEvent(OpenLogger) } + ) + } ) { Column( modifier = Modifier.padding(it) ) { - when (state.uartManagerState.connectionState) { + when (state.uartManagerState.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } GattConnectionState.STATE_DISCONNECTED, @@ -95,18 +105,6 @@ private fun PaddingBox(content: @Composable () -> Unit) { } } -@Composable -private fun AppBar(state: UARTViewState, navigateUp: () -> Unit, viewModel: UARTViewModel) { - if (state.uartManagerState.deviceName?.isNotBlank() == true) { - LoggerIconAppBar(state.uartManagerState.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { - viewModel.onEvent(OpenLogger) - } - } else { - BackIconAppBar(stringResource(id = R.string.uart_title), navigateUp) - } -} - -@OptIn(ExperimentalPagerApi::class) @Composable private fun SuccessScreen() { val input = stringResource(id = R.string.uart_input) @@ -141,10 +139,3 @@ private fun MacroView() { val state = viewModel.state.collectAsState().value MacroSection(state) { viewModel.onEvent(it) } } - -@Composable -fun Scroll(content: @Composable () -> Unit) { - Column(modifier = Modifier.verticalScroll(rememberScrollState())) { - content() - } -} diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index 69dce4b8..30e79922 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -100,7 +100,7 @@ internal class UARTViewModel @Inject constructor( repository.data.onEach { _state.value = _state.value.copy(uartManagerState = it) - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { + if (it.connectionState?.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.UART)) } }.launchIn(viewModelScope) From 221cb86cafebb7411a64999c252528470fba3265 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 18 Apr 2023 16:25:35 +0200 Subject: [PATCH 043/101] Update version catalog --- gradle.properties | 4 +++- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index 7c49c88c..e8268efb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -47,4 +47,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true # Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official + +android.nonTransitiveRClass=false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 16581bec..a2bd231e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -31,7 +31,7 @@ #Mon Feb 14 14:46:55 CET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle.kts b/settings.gradle.kts index 0aaf51a1..316156d7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.4.1") + from("no.nordicsemi.android.gradle:version-catalog:1.4.2") } } } @@ -79,6 +79,6 @@ include(":lib_utils") // includeBuild("../Android-Common-Libraries") //} -if (file("../Kotlin-BLE-Library").exists()) { - includeBuild("../Kotlin-BLE-Library") -} +//if (file("../Kotlin-BLE-Library").exists()) { +// includeBuild("../Kotlin-BLE-Library") +//} From 27a5601d2aca7edb413eb231eb8bdae941a022aa Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 20 Apr 2023 15:47:45 +0200 Subject: [PATCH 044/101] Fix crash when alarm is triggered before client is connected for PRX --- .../no/nordicsemi/android/prx/repository/PRXService.kt | 8 ++++---- settings.gradle.kts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 9cef5629..9de087b6 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -100,10 +100,6 @@ internal class PRXService : NotificationService() { .onEach { disconnect() } .launchIn(lifecycleScope) - repository.remoteAlarmLevel - .onEach { writeAlertLevel(it) } - .launchIn(lifecycleScope) - return START_REDELIVER_INTENT } @@ -180,6 +176,10 @@ internal class PRXService : NotificationService() { .filterNotNull() .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) + + repository.remoteAlarmLevel + .onEach { writeAlertLevel(it) } + .launchIn(lifecycleScope) } private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 316156d7..0ba54cf1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.4.2") + from("no.nordicsemi.android.gradle:version-catalog:1.4.3") } } } @@ -79,6 +79,6 @@ include(":lib_utils") // includeBuild("../Android-Common-Libraries") //} -//if (file("../Kotlin-BLE-Library").exists()) { -// includeBuild("../Kotlin-BLE-Library") -//} +if (file("../Kotlin-BLE-Library").exists()) { + includeBuild("../Kotlin-BLE-Library") +} From 878d36aff4f7b3995bec8bc101e76d698c1d18fe Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 24 Apr 2023 11:58:28 +0200 Subject: [PATCH 045/101] New discoveryServices() method --- .../no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt | 2 +- .../no/nordicsemi/android/cgms/repository/CGMService.kt | 2 +- .../no/nordicsemi/android/csc/repository/CSCService.kt | 2 +- .../nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt | 2 +- .../java/no/nordicsemi/android/hrs/service/HRSService.kt | 2 +- .../no/nordicsemi/android/hts/repository/HTSService.kt | 2 +- .../no/nordicsemi/android/prx/repository/PRXService.kt | 2 +- .../no/nordicsemi/android/rscs/repository/RSCSService.kt | 2 +- .../no/nordicsemi/android/uart/repository/UARTService.kt | 8 ++++++-- 9 files changed, 14 insertions(+), 10 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index bcb7e1e9..a23f0075 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -130,7 +130,7 @@ internal class BPSViewModel @Inject constructor( .onEach { logAnalytics(it.state) } .launchIn(viewModelScope) - client.services + client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } .launchIn(viewModelScope) 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 fd25f815..da8209fd 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 @@ -146,7 +146,7 @@ internal class CGMService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) - client.services + client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } .launchIn(lifecycleScope) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index fc357958..af557a69 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -102,7 +102,7 @@ internal class CSCService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) - client.services + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 03fd40e3..dfd1badc 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -168,7 +168,7 @@ internal class GLSViewModel @Inject constructor( .onEach { logAnalytics(it) } .launchIn(viewModelScope) - client.services + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } .launchIn(viewModelScope) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 5248a3c4..d9600c3e 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -104,7 +104,7 @@ internal class HRSService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) - client.services + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 3fe6d797..65c04460 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -102,7 +102,7 @@ internal class HTSService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) - client.services + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 9de087b6..3770db68 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -172,7 +172,7 @@ internal class PRXService : NotificationService() { .onEach { stopIfDisconnected(it.state, it.status) } .launchIn(lifecycleScope) - client.services + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index a3cc54cd..dbe7e2b9 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -102,7 +102,7 @@ internal class RSCSService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) - client.services + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 44303127..6e66c785 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -97,6 +97,10 @@ internal class UARTService : NotificationService() { client = device.connect(this@UARTService, logger = logger) + Log.d("AAATESTAAA","connect finish") + + client.requestMtu(Mtu.max) + repository.loggerEvent .onEach { logger.launch() } .launchIn(lifecycleScope) @@ -107,7 +111,7 @@ internal class UARTService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) - client.services + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device, logger) } .launchIn(lifecycleScope) @@ -136,7 +140,7 @@ internal class UARTService : NotificationService() { .onEach { logger.log(10, "Sent: $it") } .launchIn(lifecycleScope) - client.requestMtu(Mtu.max) + repository.onInitComplete(device) } From a2e86a819c8816b0863198a80cdb2cd67732f4f1 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 24 Apr 2023 16:54:47 +0200 Subject: [PATCH 046/101] Properly state handling in profiles --- .../java/no/nordicsemi/android/bps/view/BPSScreen.kt | 4 +++- .../nordicsemi/android/bps/viewmodel/BPSViewModel.kt | 5 +++++ .../android/cgms/repository/CGMRepository.kt | 6 ++++-- .../nordicsemi/android/cgms/repository/CGMService.kt | 11 +++++++++-- .../java/no/nordicsemi/android/cgms/view/CGMScreen.kt | 4 +++- .../nordicsemi/android/csc/repository/CSCService.kt | 5 +++++ .../java/no/nordicsemi/android/csc/view/CSCScreen.kt | 4 +++- .../no/nordicsemi/android/gls/main/view/GLSScreen.kt | 4 +++- .../android/gls/main/viewmodel/GLSViewModel.kt | 7 ++++++- .../no/nordicsemi/android/hrs/service/HRSService.kt | 5 +++++ .../java/no/nordicsemi/android/hrs/view/HRSScreen.kt | 4 +++- .../nordicsemi/android/hts/repository/HTSService.kt | 5 +++++ .../java/no/nordicsemi/android/hts/view/HTSScreen.kt | 4 +++- .../nordicsemi/android/prx/repository/PRXService.kt | 5 +++++ .../java/no/nordicsemi/android/prx/view/PRXScreen.kt | 5 +++-- .../nordicsemi/android/rscs/repository/RSCSService.kt | 5 +++++ .../no/nordicsemi/android/rscs/view/RSCSScreen.kt | 4 +++- .../nordicsemi/android/uart/repository/UARTService.kt | 9 +++++---- .../no/nordicsemi/android/uart/view/UARTScreen.kt | 9 +++------ 19 files changed, 81 insertions(+), 24 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 1218c4cf..0ea647a1 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -77,7 +77,9 @@ fun BPSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.result.connectionState?.state) { + if (state.deviceName == null) { + DeviceConnectingView { NavigateUpButton(navigateUp) } + } else when (state.result.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index a23f0075..c60640f8 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -130,6 +130,11 @@ internal class BPSViewModel @Inject constructor( .onEach { logAnalytics(it.state) } .launchIn(viewModelScope) + if (!client.isConnected) { + _state.value = _state.value.copy(deviceName = device.name) + return@launch + } + client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } 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 713a1bf5..76b70fc7 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 @@ -75,7 +75,6 @@ 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) } @@ -88,7 +87,6 @@ class CGMRepository @Inject constructor( } fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { - Log.i("AAATESTAAA", "Connection state: $connectionState") _data.value = _data.value.copy(connectionState = connectionState) } @@ -104,6 +102,10 @@ class CGMRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } + fun onInitComplete(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) + } + fun clear() { _data.value = _data.value.copy(records = emptyList()) } 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 da8209fd..097b83ee 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 @@ -146,13 +146,18 @@ internal class CGMService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) + if (!client.isConnected) { + repository.onInitComplete(device) + return@launch + } + client.discoverServices() .filterNotNull() - .onEach { configureGatt(it) } + .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val cgmService = services.findService(CGMS_SERVICE_UUID)!! val statusCharacteristic = cgmService.findCharacteristic(CGM_STATUS_UUID)!! val featureCharacteristic = cgmService.findCharacteristic(CGM_FEATURE_UUID)!! @@ -223,6 +228,8 @@ internal class CGMService : NotificationService() { if (sessionStartTime == 0L) { opsControlPointCharacteristic.write(CGMSpecificOpsControlPointData.startSession(secured).value!!) } + + repository.onInitComplete(device) } private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) = lifecycleScope.launch { 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 8555c10c..f777463a 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 @@ -81,7 +81,9 @@ fun CGMScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState?.state) { + if (state.deviceName == null) { + DeviceConnectingView { NavigateUpButton(navigateUp) } + } else when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index af557a69..afbaa898 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -102,6 +102,11 @@ internal class CSCService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) + if (!client.isConnected) { + repository.onInitComplete(device) + return@launch + } + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 47a8a668..9e745a5e 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -77,7 +77,9 @@ fun CSCScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState?.state) { + if (state.deviceName == null) { + DeviceConnectingView { NavigateUpButton(navigateUp) } + } else when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index 1aeece97..1405893b 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -77,7 +77,9 @@ fun GLSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.glsServiceData.connectionState?.state) { + if (state.deviceName == null) { + DeviceConnectingView { NavigateUpButton(navigateUp) } + } else when (state.glsServiceData.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index dfd1badc..22d73b3d 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -158,7 +158,7 @@ internal class GLSViewModel @Inject constructor( } private fun startGattClient(device: ServerDevice) = viewModelScope.launch { - logger = NordicBlekLogger(context, stringConst.APP_NAME, "BPS", device.address) + logger = NordicBlekLogger(context, stringConst.APP_NAME, "GLS", device.address) client = device.connect(context, logger = logger) @@ -168,6 +168,11 @@ internal class GLSViewModel @Inject constructor( .onEach { logAnalytics(it) } .launchIn(viewModelScope) + if (!client.isConnected) { + _state.value = _state.value.copy(deviceName = device.name) + return@launch + } + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index d9600c3e..d2a90758 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -104,6 +104,11 @@ internal class HRSService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) + if (!client.isConnected) { + repository.onInitComplete(device) + return@launch + } + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index 36394e06..daffe174 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -77,7 +77,9 @@ fun HRSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState?.state) { + if (state.deviceName == null) { + DeviceConnectingView { NavigateUpButton(navigateUp) } + } else when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 65c04460..a97d36da 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -102,6 +102,11 @@ internal class HTSService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) + if (!client.isConnected) { + repository.onInitComplete(device) + return@launch + } + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 3d320130..ef657c7a 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -81,7 +81,9 @@ fun HTSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState?.state) { + if (state.deviceName == null) { + DeviceConnectingView { NavigateUpButton(navigateUp) } + } else when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 3770db68..7d137bef 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -172,6 +172,11 @@ internal class PRXService : NotificationService() { .onEach { stopIfDisconnected(it.state, it.status) } .launchIn(lifecycleScope) + if (!client.isConnected) { + repository.onInitComplete(device) + return@launch + } + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index 23cc17f7..2aca046d 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -77,8 +77,9 @@ fun PRXScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - Log.i("AAATESTAAA", "State: ${state.connectionState?.state}") - when (state.connectionState?.state) { + if (state.deviceName == null) { + DeviceConnectingView { NavigateUpButton(navigateUp) } + } else when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index dbe7e2b9..3cad3f41 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -102,6 +102,11 @@ internal class RSCSService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) + if (!client.isConnected) { + repository.onInitComplete(device) + return@launch + } + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index dd4d4983..c9706b05 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -77,7 +77,9 @@ fun RSCSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.connectionState?.state) { + if (state.deviceName == null) { + DeviceConnectingView { NavigateUpButton(navigateUp) } + } else when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 6e66c785..cd4947a0 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -97,8 +97,6 @@ internal class UARTService : NotificationService() { client = device.connect(this@UARTService, logger = logger) - Log.d("AAATESTAAA","connect finish") - client.requestMtu(Mtu.max) repository.loggerEvent @@ -111,6 +109,11 @@ internal class UARTService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) + if (!client.isConnected) { + repository.onInitComplete(device) + return@launch + } + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device, logger) } @@ -140,8 +143,6 @@ internal class UARTService : NotificationService() { .onEach { logger.log(10, "Sent: $it") } .launchIn(lifecycleScope) - - repository.onInitComplete(device) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index aab035c0..6de05a50 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -35,8 +35,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable @@ -47,7 +45,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import com.google.accompanist.pager.ExperimentalPagerApi import no.nordicsemi.android.common.theme.view.PagerView import no.nordicsemi.android.common.theme.view.PagerViewEntity import no.nordicsemi.android.common.theme.view.PagerViewItem @@ -57,8 +54,6 @@ import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.viewmodel.UARTViewModel -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar @@ -85,7 +80,9 @@ fun UARTScreen() { Column( modifier = Modifier.padding(it) ) { - when (state.uartManagerState.connectionState?.state) { + if (state.uartManagerState.deviceName == null) { + DeviceConnectingView { NavigateUpButton(navigateUp) } + } else when (state.uartManagerState.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } GattConnectionState.STATE_DISCONNECTED, From c623e9f795f719e537f553444435aabd733d2543 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Mon, 24 Apr 2023 17:15:05 +0200 Subject: [PATCH 047/101] Fix UI when device disconnects after bonding failure --- .../nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 22d73b3d..f2f0c39e 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -166,6 +166,7 @@ internal class GLSViewModel @Inject constructor( .filterNotNull() .onEach { _state.value = _state.value.copyWithNewConnectionState(it) } .onEach { logAnalytics(it) } + .onEach { unlockUiIfDisconnected(it, device) } .launchIn(viewModelScope) if (!client.isConnected) { @@ -185,6 +186,12 @@ internal class GLSViewModel @Inject constructor( } } + private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { + if (connectionState.state == GattConnectionState.STATE_CONNECTED) { + _state.value = _state.value.copy(deviceName = device.name) + } + } + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val glsService = services.findService(GLS_SERVICE_UUID)!! glucoseMeasurementCharacteristic = glsService.findCharacteristic(GM_CHARACTERISTIC)!! From 0d6035514d004d157c9007309aef2595dc13a9b5 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 25 Apr 2023 15:25:23 +0200 Subject: [PATCH 048/101] Fix UI when bonding required --- .../java/no/nordicsemi/android/gls/main/view/GLSScreen.kt | 1 + .../no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index 1405893b..3a4a0f07 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.gls.main.view +import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index f2f0c39e..7a313db5 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -39,6 +39,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterNotNull @@ -174,6 +175,8 @@ internal class GLSViewModel @Inject constructor( return@launch } + client.waitForBonding() + client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } @@ -187,7 +190,7 @@ internal class GLSViewModel @Inject constructor( } private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_CONNECTED) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { _state.value = _state.value.copy(deviceName = device.name) } } From 43c04fc79acefa4e59199a35aade9bc26d35a631 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 25 Apr 2023 16:02:33 +0200 Subject: [PATCH 049/101] Add unlockUiIfDisconnected to BPS --- .../no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index c60640f8..a6d3b454 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -128,6 +128,7 @@ internal class BPSViewModel @Inject constructor( .onEach { onDataUpdate(it) } .onEach { stopIfDisconnected(it.state) } .onEach { logAnalytics(it.state) } + .onEach { unlockUiIfDisconnected(it, device) } .launchIn(viewModelScope) if (!client.isConnected) { @@ -141,6 +142,12 @@ internal class BPSViewModel @Inject constructor( .launchIn(viewModelScope) } + private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + _state.value = _state.value.copy(deviceName = device.name) + } + } + private suspend fun configureGatt(services: BleGattServices) { val bpsService = services.findService(BPS_SERVICE_UUID)!! val bpmCharacteristic = bpsService.findCharacteristic(BPM_CHARACTERISTIC_UUID)!! From 52f4e26665f94545ab4ac586d07f0b694fd518e5 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 25 Apr 2023 16:50:10 +0200 Subject: [PATCH 050/101] Unlock UI if device disconnects after connection --- .../no/nordicsemi/android/cgms/repository/CGMService.kt | 7 +++++++ .../no/nordicsemi/android/csc/repository/CSCService.kt | 7 +++++++ .../java/no/nordicsemi/android/hrs/service/HRSService.kt | 7 +++++++ .../no/nordicsemi/android/hts/repository/HTSService.kt | 7 +++++++ .../no/nordicsemi/android/rscs/repository/RSCSService.kt | 7 +++++++ .../no/nordicsemi/android/uart/repository/UARTService.kt | 7 +++++++ 6 files changed, 42 insertions(+) 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 097b83ee..6f5658b5 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 @@ -144,6 +144,7 @@ internal class CGMService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } + .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { @@ -312,6 +313,12 @@ internal class CGMService : NotificationService() { } } + private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + repository.onInitComplete(device) + } + } + private fun disconnect() { client.disconnect() } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index afbaa898..8cb71746 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -100,6 +100,7 @@ internal class CSCService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } + .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { @@ -139,6 +140,12 @@ internal class CSCService : NotificationService() { } } + private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + repository.onInitComplete(device) + } + } + private fun disconnect() { client.disconnect() } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index d2a90758..f7331c13 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -102,6 +102,7 @@ internal class HRSService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } + .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { @@ -144,6 +145,12 @@ internal class HRSService : NotificationService() { } } + private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + repository.onInitComplete(device) + } + } + private fun disconnect() { client.disconnect() } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index a97d36da..01a4d47b 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -100,6 +100,7 @@ internal class HTSService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } + .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { @@ -138,6 +139,12 @@ internal class HTSService : NotificationService() { } } + private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + repository.onInitComplete(device) + } + } + private fun disconnect() { client.disconnect() } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index 3cad3f41..d0d26e12 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -100,6 +100,7 @@ internal class RSCSService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } + .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { @@ -138,6 +139,12 @@ internal class RSCSService : NotificationService() { } } + private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + repository.onInitComplete(device) + } + } + private fun disconnect() { client.disconnect() } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index cd4947a0..50b60814 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -107,6 +107,7 @@ internal class UARTService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } + .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { @@ -160,6 +161,12 @@ internal class UARTService : NotificationService() { } } + private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + repository.onInitComplete(device) + } + } + private fun disconnect() { client.disconnect() } From 4579ade6335f8e761994adeeca197b40685acc2c Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 26 Apr 2023 11:49:33 +0200 Subject: [PATCH 051/101] Fix padding with scroll --- .../src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt | 2 +- .../src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt | 2 +- .../src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt | 2 +- .../main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt | 2 +- .../src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt | 2 +- .../src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt | 2 +- .../src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt | 2 +- .../src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 0ea647a1..03d19b54 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -74,8 +74,8 @@ fun BPSScreen() { Column( modifier = Modifier .padding(it) - .padding(16.dp) .verticalScroll(rememberScrollState()) + .padding(16.dp) ) { if (state.deviceName == null) { DeviceConnectingView { NavigateUpButton(navigateUp) } 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 f777463a..3fb0e623 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 @@ -78,8 +78,8 @@ fun CGMScreen() { Column( modifier = Modifier .padding(it) - .padding(16.dp) .verticalScroll(rememberScrollState()) + .padding(16.dp) ) { if (state.deviceName == null) { DeviceConnectingView { NavigateUpButton(navigateUp) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 9e745a5e..e8967219 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -74,8 +74,8 @@ fun CSCScreen() { Column( modifier = Modifier .padding(it) - .padding(16.dp) .verticalScroll(rememberScrollState()) + .padding(16.dp) ) { if (state.deviceName == null) { DeviceConnectingView { NavigateUpButton(navigateUp) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index 3a4a0f07..8e105581 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -75,8 +75,8 @@ fun GLSScreen() { Column( modifier = Modifier .padding(it) - .padding(16.dp) .verticalScroll(rememberScrollState()) + .padding(16.dp) ) { if (state.deviceName == null) { DeviceConnectingView { NavigateUpButton(navigateUp) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index daffe174..3219714d 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -74,8 +74,8 @@ fun HRSScreen() { Column( modifier = Modifier .padding(it) - .padding(16.dp) .verticalScroll(rememberScrollState()) + .padding(16.dp) ) { if (state.deviceName == null) { DeviceConnectingView { NavigateUpButton(navigateUp) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index ef657c7a..9e921911 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -78,8 +78,8 @@ fun HTSScreen() { Column( modifier = Modifier .padding(it) - .padding(16.dp) .verticalScroll(rememberScrollState()) + .padding(16.dp) ) { if (state.deviceName == null) { DeviceConnectingView { NavigateUpButton(navigateUp) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index 2aca046d..64140408 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -74,8 +74,8 @@ fun PRXScreen() { Column( modifier = Modifier .padding(it) - .padding(16.dp) .verticalScroll(rememberScrollState()) + .padding(16.dp) ) { if (state.deviceName == null) { DeviceConnectingView { NavigateUpButton(navigateUp) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index c9706b05..06e9c1a0 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -74,8 +74,8 @@ fun RSCSScreen() { Column( modifier = Modifier .padding(it) - .padding(16.dp) .verticalScroll(rememberScrollState()) + .padding(16.dp) ) { if (state.deviceName == null) { DeviceConnectingView { NavigateUpButton(navigateUp) } From fde67ff5478141b913caabbafed3d51a12760fa1 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 26 Apr 2023 11:53:02 +0200 Subject: [PATCH 052/101] Clear repositories correctly --- .../java/no/nordicsemi/android/cgms/repository/CGMRepository.kt | 1 + .../java/no/nordicsemi/android/csc/repository/CSCRepository.kt | 1 + .../main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt | 1 + .../java/no/nordicsemi/android/hts/repository/HTSRepository.kt | 1 + .../java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt | 1 + .../java/no/nordicsemi/android/uart/repository/UARTRepository.kt | 1 + 6 files changed, 6 insertions(+) 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 76b70fc7..24d76fb3 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 @@ -111,6 +111,7 @@ class CGMRepository @Inject constructor( } fun release() { + _data.value = CGMServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 8b1b36ac..0f009ec8 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -106,6 +106,7 @@ class CSCRepository @Inject constructor( } fun release() { + _data.value = CSCServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 47d84a69..4de965a0 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -102,6 +102,7 @@ class HRSRepository @Inject constructor( } fun release() { + _data.value = HRSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 89322d7d..d3cf7635 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -100,6 +100,7 @@ class HTSRepository @Inject constructor( fun release() { logger = null + _data.value = HTSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 1d1950d0..10bc3004 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -95,6 +95,7 @@ class RSCSRepository @Inject constructor( fun release() { logger = null + _data.value = RSCSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index e59d2c79..abcba7a8 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -125,6 +125,7 @@ class UARTRepository @Inject internal constructor( } fun release() { + _data.value = UARTServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } From 9354dd0b44fd0266c03b343039f139afe5be6657 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 26 Apr 2023 12:34:06 +0200 Subject: [PATCH 053/101] Clear repository when enter a screen --- .../android/cgms/repository/CGMRepository.kt | 10 ++++++---- .../nordicsemi/android/cgms/repository/CGMService.kt | 2 +- .../nordicsemi/android/cgms/viewmodel/CGMViewModel.kt | 4 +++- .../nordicsemi/android/csc/repository/CSCRepository.kt | 9 ++++++--- .../nordicsemi/android/csc/viewmodel/CSCViewModel.kt | 4 +++- .../no/nordicsemi/android/hrs/service/HRSRepository.kt | 7 +++++-- .../nordicsemi/android/hrs/viewmodel/HRSViewModel.kt | 4 +++- .../nordicsemi/android/hts/repository/HTSRepository.kt | 7 +++++-- .../nordicsemi/android/hts/viewmodel/HTSViewModel.kt | 4 +++- .../nordicsemi/android/prx/repository/PRXRepository.kt | 7 +++++-- .../no/nordicsemi/android/prx/repository/PRXService.kt | 2 +- .../nordicsemi/android/prx/viewmodel/PRXViewModel.kt | 4 +++- .../android/rscs/repository/RSCSRepository.kt | 7 +++++-- .../nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt | 4 +++- .../android/uart/repository/UARTRepository.kt | 7 +++++-- .../nordicsemi/android/uart/viewmodel/UARTViewModel.kt | 4 +++- 16 files changed, 60 insertions(+), 26 deletions(-) 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 24d76fb3..043cf604 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 @@ -32,7 +32,6 @@ package no.nordicsemi.android.cgms.repository import android.content.Context -import android.util.Log import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -106,12 +105,15 @@ class CGMRepository @Inject constructor( _data.value = _data.value.copy(deviceName = device.name) } - fun clear() { + fun clearRecords() { _data.value = _data.value.copy(records = emptyList()) } - fun release() { - _data.value = CGMServiceData() + fun stop() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + fun clear() { + _data.value = CGMServiceData() + } } 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 6f5658b5..47f38a94 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 @@ -286,7 +286,7 @@ internal class CGMService : NotificationService() { } private fun clear() { - repository.clear() + repository.clearRecords() } private suspend fun requestLastRecord() { 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 abe520d8..330bd397 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 @@ -67,6 +67,8 @@ internal class CGMViewModel @Inject constructor( val state = repository.data init { + repository.clear() + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -113,6 +115,6 @@ internal class CGMViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.stop() } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 0f009ec8..979305a6 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -38,7 +38,6 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -105,8 +104,12 @@ class CSCRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { - _data.value = CSCServiceData() + fun stop() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + fun clear() { + _wheelSize.value = WheelSizes.default + _data.value = CSCServiceData() + } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index 47e61ed5..e1817e12 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -68,6 +68,8 @@ internal class CSCViewModel @Inject constructor( val state = repository.data init { + repository.clear() + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -115,7 +117,7 @@ internal class CSCViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.stop() navigationManager.navigateUp() } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 4de965a0..a2aa271d 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -101,8 +101,11 @@ class HRSRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { - _data.value = HRSServiceData() + fun stop() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + fun clear() { + _data.value = HRSServiceData() + } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index 3168334b..44310e26 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -66,6 +66,8 @@ internal class HRSViewModel @Inject constructor( val state = repository.data init { + repository.clear() + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -112,7 +114,7 @@ internal class HRSViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.stop() navigationManager.navigateUp() } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index d3cf7635..27d0f47d 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -98,9 +98,12 @@ class HTSRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { + fun stop() { logger = null - _data.value = HTSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + fun clear() { + _data.value = HTSServiceData() + } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index 8d28b961..03bf9d87 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -66,6 +66,8 @@ internal class HTSViewModel @Inject constructor( val state = repository.data init { + repository.clear() + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -108,7 +110,7 @@ internal class HTSViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.stop() navigationManager.navigateUp() } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 183a53af..96577ca2 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -106,9 +106,12 @@ class PRXRepository @Inject internal constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { - _data.value = PRXServiceData() + fun stop() { _remoteAlarmLevel.tryEmit(AlarmLevel.NONE) _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + fun clear() { + _data.value = PRXServiceData() + } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 7d137bef..416c5af3 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -213,7 +213,7 @@ internal class PRXService : NotificationService() { private fun stopIfDisconnected(connectionState: GattConnectionState, connectionStatus: BleGattConnectionStatus) { if (connectionState == GattConnectionState.STATE_DISCONNECTED && !connectionStatus.isLinkLoss) { server.stopServer() - repository.release() + repository.stop() stopSelf() } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index 53e86ce7..42c5e551 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -72,6 +72,8 @@ internal class PRXViewModel @Inject constructor( val state = repository.data init { + repository.clear() + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -123,7 +125,7 @@ internal class PRXViewModel @Inject constructor( private fun disconnect() { alarmHandler.pauseAlarm() navigationManager.navigateUp() - repository.release() + repository.stop() } override fun onCleared() { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 10bc3004..4db977d4 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -93,9 +93,12 @@ class RSCSRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { + fun stop() { logger = null - _data.value = RSCSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + fun clear() { + _data.value = RSCSServiceData() + } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 57d912ac..9b0faf5f 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -65,6 +65,8 @@ internal class RSCSViewModel @Inject constructor( val state = repository.data init { + repository.clear() + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -106,7 +108,7 @@ internal class RSCSViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.stop() navigationManager.navigateUp() } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index abcba7a8..316faafc 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -124,8 +124,11 @@ class UARTRepository @Inject internal constructor( configurationDataSource.saveConfigurationName(name) } - fun release() { - _data.value = UARTServiceData() + fun stop() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + fun clear() { + _data.value = UARTServiceData() + } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index 30e79922..cf05630e 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -91,6 +91,8 @@ internal class UARTViewModel @Inject constructor( val state = _state.asStateFlow() init { + repository.clear() + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -233,7 +235,7 @@ internal class UARTViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.stop() navigationManager.navigateUp() } } From c5b808cd4f58f04cb01335fe7eecfd69453c17ce Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 26 Apr 2023 12:44:28 +0200 Subject: [PATCH 054/101] Revert "Clear repository when enter a screen" This reverts commit 9354dd0b44fd0266c03b343039f139afe5be6657. --- .../android/cgms/repository/CGMRepository.kt | 10 ++++------ .../nordicsemi/android/cgms/repository/CGMService.kt | 2 +- .../nordicsemi/android/cgms/viewmodel/CGMViewModel.kt | 4 +--- .../nordicsemi/android/csc/repository/CSCRepository.kt | 9 +++------ .../nordicsemi/android/csc/viewmodel/CSCViewModel.kt | 4 +--- .../no/nordicsemi/android/hrs/service/HRSRepository.kt | 7 ++----- .../nordicsemi/android/hrs/viewmodel/HRSViewModel.kt | 4 +--- .../nordicsemi/android/hts/repository/HTSRepository.kt | 7 ++----- .../nordicsemi/android/hts/viewmodel/HTSViewModel.kt | 4 +--- .../nordicsemi/android/prx/repository/PRXRepository.kt | 7 ++----- .../no/nordicsemi/android/prx/repository/PRXService.kt | 2 +- .../nordicsemi/android/prx/viewmodel/PRXViewModel.kt | 4 +--- .../android/rscs/repository/RSCSRepository.kt | 7 ++----- .../nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt | 4 +--- .../android/uart/repository/UARTRepository.kt | 7 ++----- .../nordicsemi/android/uart/viewmodel/UARTViewModel.kt | 4 +--- 16 files changed, 26 insertions(+), 60 deletions(-) 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 043cf604..24d76fb3 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 @@ -32,6 +32,7 @@ package no.nordicsemi.android.cgms.repository import android.content.Context +import android.util.Log import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -105,15 +106,12 @@ class CGMRepository @Inject constructor( _data.value = _data.value.copy(deviceName = device.name) } - fun clearRecords() { + fun clear() { _data.value = _data.value.copy(records = emptyList()) } - fun stop() { + fun release() { + _data.value = CGMServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } - - fun clear() { - _data.value = CGMServiceData() - } } 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 47f38a94..6f5658b5 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 @@ -286,7 +286,7 @@ internal class CGMService : NotificationService() { } private fun clear() { - repository.clearRecords() + repository.clear() } private suspend fun requestLastRecord() { 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 330bd397..abe520d8 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 @@ -67,8 +67,6 @@ internal class CGMViewModel @Inject constructor( val state = repository.data init { - repository.clear() - viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -115,6 +113,6 @@ internal class CGMViewModel @Inject constructor( } private fun disconnect() { - repository.stop() + repository.release() } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 979305a6..0f009ec8 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -104,12 +105,8 @@ class CSCRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun stop() { + fun release() { + _data.value = CSCServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } - - fun clear() { - _wheelSize.value = WheelSizes.default - _data.value = CSCServiceData() - } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index e1817e12..47e61ed5 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -68,8 +68,6 @@ internal class CSCViewModel @Inject constructor( val state = repository.data init { - repository.clear() - viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -117,7 +115,7 @@ internal class CSCViewModel @Inject constructor( } private fun disconnect() { - repository.stop() + repository.release() navigationManager.navigateUp() } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index a2aa271d..4de965a0 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -101,11 +101,8 @@ class HRSRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun stop() { + fun release() { + _data.value = HRSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } - - fun clear() { - _data.value = HRSServiceData() - } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index 44310e26..3168334b 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -66,8 +66,6 @@ internal class HRSViewModel @Inject constructor( val state = repository.data init { - repository.clear() - viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -114,7 +112,7 @@ internal class HRSViewModel @Inject constructor( } private fun disconnect() { - repository.stop() + repository.release() navigationManager.navigateUp() } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 27d0f47d..d3cf7635 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -98,12 +98,9 @@ class HTSRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun stop() { + fun release() { logger = null + _data.value = HTSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } - - fun clear() { - _data.value = HTSServiceData() - } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index 03bf9d87..8d28b961 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -66,8 +66,6 @@ internal class HTSViewModel @Inject constructor( val state = repository.data init { - repository.clear() - viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -110,7 +108,7 @@ internal class HTSViewModel @Inject constructor( } private fun disconnect() { - repository.stop() + repository.release() navigationManager.navigateUp() } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 96577ca2..183a53af 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -106,12 +106,9 @@ class PRXRepository @Inject internal constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun stop() { + fun release() { + _data.value = PRXServiceData() _remoteAlarmLevel.tryEmit(AlarmLevel.NONE) _stopEvent.tryEmit(DisconnectAndStopEvent()) } - - fun clear() { - _data.value = PRXServiceData() - } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 416c5af3..7d137bef 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -213,7 +213,7 @@ internal class PRXService : NotificationService() { private fun stopIfDisconnected(connectionState: GattConnectionState, connectionStatus: BleGattConnectionStatus) { if (connectionState == GattConnectionState.STATE_DISCONNECTED && !connectionStatus.isLinkLoss) { server.stopServer() - repository.stop() + repository.release() stopSelf() } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index 42c5e551..53e86ce7 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -72,8 +72,6 @@ internal class PRXViewModel @Inject constructor( val state = repository.data init { - repository.clear() - viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -125,7 +123,7 @@ internal class PRXViewModel @Inject constructor( private fun disconnect() { alarmHandler.pauseAlarm() navigationManager.navigateUp() - repository.stop() + repository.release() } override fun onCleared() { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 4db977d4..10bc3004 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -93,12 +93,9 @@ class RSCSRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun stop() { + fun release() { logger = null + _data.value = RSCSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } - - fun clear() { - _data.value = RSCSServiceData() - } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 9b0faf5f..57d912ac 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -65,8 +65,6 @@ internal class RSCSViewModel @Inject constructor( val state = repository.data init { - repository.clear() - viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -108,7 +106,7 @@ internal class RSCSViewModel @Inject constructor( } private fun disconnect() { - repository.stop() + repository.release() navigationManager.navigateUp() } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 316faafc..abcba7a8 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -124,11 +124,8 @@ class UARTRepository @Inject internal constructor( configurationDataSource.saveConfigurationName(name) } - fun stop() { + fun release() { + _data.value = UARTServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } - - fun clear() { - _data.value = UARTServiceData() - } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index cf05630e..30e79922 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -91,8 +91,6 @@ internal class UARTViewModel @Inject constructor( val state = _state.asStateFlow() init { - repository.clear() - viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -235,7 +233,7 @@ internal class UARTViewModel @Inject constructor( } private fun disconnect() { - repository.stop() + repository.release() navigationManager.navigateUp() } } From 1eee7962f527cbe59564c0f32b2caedc29e0a803 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 26 Apr 2023 12:44:28 +0200 Subject: [PATCH 055/101] Revert "Clear repositories correctly" This reverts commit fde67ff5478141b913caabbafed3d51a12760fa1. --- .../java/no/nordicsemi/android/cgms/repository/CGMRepository.kt | 1 - .../java/no/nordicsemi/android/csc/repository/CSCRepository.kt | 1 - .../main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt | 1 - .../java/no/nordicsemi/android/hts/repository/HTSRepository.kt | 1 - .../java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt | 1 - .../java/no/nordicsemi/android/uart/repository/UARTRepository.kt | 1 - 6 files changed, 6 deletions(-) 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 24d76fb3..76b70fc7 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 @@ -111,7 +111,6 @@ class CGMRepository @Inject constructor( } fun release() { - _data.value = CGMServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 0f009ec8..8b1b36ac 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -106,7 +106,6 @@ class CSCRepository @Inject constructor( } fun release() { - _data.value = CSCServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 4de965a0..47d84a69 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -102,7 +102,6 @@ class HRSRepository @Inject constructor( } fun release() { - _data.value = HRSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index d3cf7635..89322d7d 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -100,7 +100,6 @@ class HTSRepository @Inject constructor( fun release() { logger = null - _data.value = HTSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 10bc3004..1d1950d0 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -95,7 +95,6 @@ class RSCSRepository @Inject constructor( fun release() { logger = null - _data.value = RSCSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index abcba7a8..e59d2c79 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -125,7 +125,6 @@ class UARTRepository @Inject internal constructor( } fun release() { - _data.value = UARTServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } From a7ba6fb64cafb43f6249ad12524ae8044e2922ac Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 26 Apr 2023 12:48:00 +0200 Subject: [PATCH 056/101] Revert "Revert "Clear repositories correctly"" This reverts commit 1eee7962f527cbe59564c0f32b2caedc29e0a803. --- .../java/no/nordicsemi/android/cgms/repository/CGMRepository.kt | 1 + .../java/no/nordicsemi/android/csc/repository/CSCRepository.kt | 1 + .../main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt | 1 + .../java/no/nordicsemi/android/hts/repository/HTSRepository.kt | 1 + .../java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt | 1 + .../java/no/nordicsemi/android/uart/repository/UARTRepository.kt | 1 + 6 files changed, 6 insertions(+) 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 76b70fc7..24d76fb3 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 @@ -111,6 +111,7 @@ class CGMRepository @Inject constructor( } fun release() { + _data.value = CGMServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 8b1b36ac..0f009ec8 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -106,6 +106,7 @@ class CSCRepository @Inject constructor( } fun release() { + _data.value = CSCServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 47d84a69..4de965a0 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -102,6 +102,7 @@ class HRSRepository @Inject constructor( } fun release() { + _data.value = HRSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 89322d7d..d3cf7635 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -100,6 +100,7 @@ class HTSRepository @Inject constructor( fun release() { logger = null + _data.value = HTSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 1d1950d0..10bc3004 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -95,6 +95,7 @@ class RSCSRepository @Inject constructor( fun release() { logger = null + _data.value = RSCSServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index e59d2c79..abcba7a8 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -125,6 +125,7 @@ class UARTRepository @Inject internal constructor( } fun release() { + _data.value = UARTServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } } From 1155359dd5b807ad9c710985e3449e816f85ccb9 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 26 Apr 2023 15:32:59 +0200 Subject: [PATCH 057/101] Prevent calling init after disconnect --- .../no/nordicsemi/android/cgms/repository/CGMService.kt | 6 +++++- .../nordicsemi/android/csc/repository/CSCRepository.kt | 9 +++++++-- .../no/nordicsemi/android/csc/repository/CSCService.kt | 7 ++++++- .../java/no/nordicsemi/android/csc/view/CSCScreen.kt | 3 +++ .../java/no/nordicsemi/android/hrs/service/HRSService.kt | 6 +++++- .../no/nordicsemi/android/hts/repository/HTSService.kt | 6 +++++- .../no/nordicsemi/android/prx/repository/PRXService.kt | 4 ++++ .../no/nordicsemi/android/rscs/repository/RSCSService.kt | 6 +++++- .../no/nordicsemi/android/uart/repository/UARTService.kt | 6 +++++- 9 files changed, 45 insertions(+), 8 deletions(-) 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 6f5658b5..e43cf74f 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 @@ -104,6 +104,8 @@ internal class CGMService : NotificationService() { private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic + private var hasBeenInitialized: Boolean = false + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -148,6 +150,7 @@ internal class CGMService : NotificationService() { .launchIn(lifecycleScope) if (!client.isConnected) { + hasBeenInitialized = true repository.onInitComplete(device) return@launch } @@ -230,6 +233,7 @@ internal class CGMService : NotificationService() { opsControlPointCharacteristic.write(CGMSpecificOpsControlPointData.startSession(secured).value!!) } + hasBeenInitialized = true repository.onInitComplete(device) } @@ -314,7 +318,7 @@ internal class CGMService : NotificationService() { } private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { repository.onInitComplete(device) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 0f009ec8..94544f15 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -32,13 +32,13 @@ package no.nordicsemi.android.csc.repository import android.content.Context +import android.util.Log import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -78,7 +78,11 @@ class CSCRepository @Inject constructor( } fun onInitComplete(device: ServerDevice) { - _data.value = _data.value.copy(deviceName = device.name) + Log.d("AAATESTAAA", "onInitComplete: ${data.value}") + if (_data.value.deviceName == null) { + Log.d("AAATESTAAA", "AAA") + _data.value = _data.value.copy(deviceName = device.name) + } } internal fun setSpeedUnit(speedUnit: SpeedUnit) { @@ -106,6 +110,7 @@ class CSCRepository @Inject constructor( } fun release() { + Log.d("AAATESTAAA", "release: ${data.value}") _data.value = CSCServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 8cb71746..5e21dd1d 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -73,6 +73,8 @@ internal class CSCService : NotificationService() { private lateinit var client: BleGattClient + private var hasBeenInitialized: Boolean = false + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -104,6 +106,7 @@ internal class CSCService : NotificationService() { .launchIn(lifecycleScope) if (!client.isConnected) { + hasBeenInitialized = true repository.onInitComplete(device) return@launch } @@ -131,6 +134,7 @@ internal class CSCService : NotificationService() { .onEach { repository.onCSCDataChanged(it) } .launchIn(lifecycleScope) + hasBeenInitialized = true repository.onInitComplete(device) } @@ -141,7 +145,8 @@ internal class CSCService : NotificationService() { } private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { + hasBeenInitialized = true repository.onInitComplete(device) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index e8967219..7c454235 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.csc.view +import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -57,6 +58,8 @@ fun CSCScreen() { val viewModel: CSCViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value + Log.d("AAATESTAAA", "State: ${state}") + val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index f7331c13..2a6588e8 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -75,6 +75,8 @@ internal class HRSService : NotificationService() { private lateinit var client: BleGattClient + private var hasBeenInitialized: Boolean = false + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -106,6 +108,7 @@ internal class HRSService : NotificationService() { .launchIn(lifecycleScope) if (!client.isConnected) { + hasBeenInitialized = true repository.onInitComplete(device) return@launch } @@ -136,6 +139,7 @@ internal class HRSService : NotificationService() { .onEach { repository.onHRSDataChanged(it) } .launchIn(lifecycleScope) + hasBeenInitialized = true repository.onInitComplete(device) } @@ -146,7 +150,7 @@ internal class HRSService : NotificationService() { } private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { repository.onInitComplete(device) } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 01a4d47b..107aef99 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -73,6 +73,8 @@ internal class HTSService : NotificationService() { private lateinit var client: BleGattClient + private var hasBeenInitialized: Boolean = false + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -104,6 +106,7 @@ internal class HTSService : NotificationService() { .launchIn(lifecycleScope) if (!client.isConnected) { + hasBeenInitialized = true repository.onInitComplete(device) return@launch } @@ -130,6 +133,7 @@ internal class HTSService : NotificationService() { .onEach { repository.onHTSDataChanged(it) } .launchIn(lifecycleScope) + hasBeenInitialized = true repository.onInitComplete(device) } @@ -140,7 +144,7 @@ internal class HTSService : NotificationService() { } private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { repository.onInitComplete(device) } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 7d137bef..96f668d3 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -89,6 +89,8 @@ internal class PRXService : NotificationService() { private lateinit var alertLevelCharacteristic: BleGattCharacteristic + private var hasBeenInitialized: Boolean = false + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -173,6 +175,7 @@ internal class PRXService : NotificationService() { .launchIn(lifecycleScope) if (!client.isConnected) { + hasBeenInitialized = true repository.onInitComplete(device) return@launch } @@ -202,6 +205,7 @@ internal class PRXService : NotificationService() { linkLossCharacteristic.write(AlertLevelInputParser.parse(AlarmLevel.HIGH)) + hasBeenInitialized = true repository.onInitComplete(device) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index d0d26e12..0056160d 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -73,6 +73,8 @@ internal class RSCSService : NotificationService() { private lateinit var client: BleGattClient + private var hasBeenInitialized: Boolean = false + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -104,6 +106,7 @@ internal class RSCSService : NotificationService() { .launchIn(lifecycleScope) if (!client.isConnected) { + hasBeenInitialized = true repository.onInitComplete(device) return@launch } @@ -130,6 +133,7 @@ internal class RSCSService : NotificationService() { .onEach { repository.onRSCSDataChanged(it) } .launchIn(lifecycleScope) + hasBeenInitialized = true repository.onInitComplete(device) } @@ -140,7 +144,7 @@ internal class RSCSService : NotificationService() { } private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { repository.onInitComplete(device) } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 50b60814..ae63213c 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -78,6 +78,8 @@ internal class UARTService : NotificationService() { private lateinit var client: BleGattClient + private var hasBeenInitialized: Boolean = false + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -111,6 +113,7 @@ internal class UARTService : NotificationService() { .launchIn(lifecycleScope) if (!client.isConnected) { + hasBeenInitialized = true repository.onInitComplete(device) return@launch } @@ -144,6 +147,7 @@ internal class UARTService : NotificationService() { .onEach { logger.log(10, "Sent: $it") } .launchIn(lifecycleScope) + hasBeenInitialized = true repository.onInitComplete(device) } @@ -162,7 +166,7 @@ internal class UARTService : NotificationService() { } private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { + if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { repository.onInitComplete(device) } } From 79cb9d8a1ebf300186e802d0a8cd157ae003ed7b Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 26 Apr 2023 16:43:56 +0200 Subject: [PATCH 058/101] Use wait for bonding properly --- .../no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt | 4 ++-- .../main/java/no/nordicsemi/android/hrs/service/HRSService.kt | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 7a313db5..c10be870 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -170,13 +170,13 @@ internal class GLSViewModel @Inject constructor( .onEach { unlockUiIfDisconnected(it, device) } .launchIn(viewModelScope) + client.waitForBonding() + if (!client.isConnected) { _state.value = _state.value.copy(deviceName = device.name) return@launch } - client.waitForBonding() - client.discoverServices() .filterNotNull() .onEach { configureGatt(it, device) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 2a6588e8..266c9cce 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -107,6 +107,8 @@ internal class HRSService : NotificationService() { .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) + client.waitForBonding() + if (!client.isConnected) { hasBeenInitialized = true repository.onInitComplete(device) From 2a3c8ec477176d3f17206b5cf38923efe4044a39 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 27 Apr 2023 10:21:53 +0200 Subject: [PATCH 059/101] Fix log tag --- .../main/java/no/nordicsemi/android/hrs/service/HRSService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 266c9cce..21489cae 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -92,7 +92,7 @@ internal class HRSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicBlekLogger(this@HRSService, stringConst.APP_NAME, "CSC", device.address) + val logger = NordicBlekLogger(this@HRSService, stringConst.APP_NAME, "HRS", device.address) client = device.connect(this@HRSService, logger = logger) From 98ed08a03739407741208113b11e7ce6caea6b69 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 27 Apr 2023 10:30:15 +0200 Subject: [PATCH 060/101] Fix spelling error --- .../java/no/nordicsemi/android/hrs/service/HRSService.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 21489cae..18166de5 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -122,9 +122,9 @@ internal class HRSService : NotificationService() { } private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { - val htsService = services.findService(HRS_SERVICE_UUID)!! - val htsMeasurementCharacteristic = htsService.findCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID)!! - val bodySensorLocationCharacteristic = htsService.findCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID)!! + val hrsService = services.findService(HRS_SERVICE_UUID)!! + val hrsMeasurementCharacteristic = hrsService.findCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID)!! + val bodySensorLocationCharacteristic = hrsService.findCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! @@ -136,7 +136,7 @@ internal class HRSService : NotificationService() { .onEach { repository.onBatteryLevelChanged(it) } .launchIn(lifecycleScope) - htsMeasurementCharacteristic.getNotifications() + hrsMeasurementCharacteristic.getNotifications() .mapNotNull { HRSDataParser.parse(it) } .onEach { repository.onHRSDataChanged(it) } .launchIn(lifecycleScope) From 1d8181c968cac2dc3ef2e8def54aae659fa1807e Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 27 Apr 2023 10:52:32 +0200 Subject: [PATCH 061/101] Fix state on HTS profile --- .../android/hts/data/HTSServiceData.kt | 3 +-- .../android/hts/repository/HTSRepository.kt | 4 ---- .../android/hts/repository/HTSService.kt | 16 ++++------------ .../no/nordicsemi/android/hts/view/HTSScreen.kt | 9 +++------ .../android/hts/viewmodel/HTSViewModel.kt | 6 ++++++ 5 files changed, 14 insertions(+), 24 deletions(-) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt index 750fd306..11fcc397 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt @@ -40,6 +40,5 @@ internal data class HTSServiceData( val data: HTSData = HTSData(), val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, - val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS, - val deviceName: String? = null + val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS ) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index d3cf7635..4e4224fe 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -74,10 +74,6 @@ class HTSRepository @Inject constructor( serviceManager.startService(HTSService::class.java, device) } - fun onInitComplete(device: ServerDevice) { - _data.value = _data.value.copy(deviceName = device.name) - } - internal fun setTemperatureUnit(temperatureUnit: TemperatureUnit) { _data.value = _data.value.copy(temperatureUnit = temperatureUnit) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 107aef99..349620a2 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -33,6 +33,7 @@ package no.nordicsemi.android.hts.repository import android.annotation.SuppressLint import android.content.Intent +import android.util.Log import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.filterNotNull @@ -94,20 +95,20 @@ internal class HTSService : NotificationService() { client = device.connect(this@HTSService, logger = logger) + client.waitForBonding() + repository.loggerEvent .onEach { logger.launch() } .launchIn(lifecycleScope) client.connectionStateWithStatus + .onEach { Log.d("AAATESTAAA", "Connection state: $it") } .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } - .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { - hasBeenInitialized = true - repository.onInitComplete(device) return@launch } @@ -132,9 +133,6 @@ internal class HTSService : NotificationService() { .mapNotNull { HTSDataParser.parse(it) } .onEach { repository.onHTSDataChanged(it) } .launchIn(lifecycleScope) - - hasBeenInitialized = true - repository.onInitComplete(device) } private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { @@ -143,12 +141,6 @@ internal class HTSService : NotificationService() { } } - private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { - repository.onInitComplete(device) - } - } - private fun disconnect() { client.disconnect() } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 9e921911..be224b0e 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -40,18 +40,14 @@ import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.hts.R -import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.hts.viewmodel.HTSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar @@ -60,13 +56,14 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun HTSScreen() { val viewModel: HTSViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value + val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( topBar = { ProfileAppBar( - deviceName = state.deviceName, + deviceName = deviceName, connectionState = state.connectionState, title = R.string.hts_title, navigateUp = navigateUp, @@ -81,7 +78,7 @@ fun HTSScreen() { .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - if (state.deviceName == null) { + if (deviceName == null) { DeviceConnectingView { NavigateUpButton(navigateUp) } } else when (state.connectionState?.state) { null, diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index 8d28b961..fbc760f1 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -35,6 +35,8 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -65,6 +67,9 @@ internal class HTSViewModel @Inject constructor( val state = repository.data + private val _deviceName = MutableStateFlow(null) + val deviceName = _deviceName.asStateFlow() + init { viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { @@ -95,6 +100,7 @@ internal class HTSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { + _deviceName.value = device.name repository.launch(device) } From 77c0d057b52f8501f6aab298b1e754d84c84071b Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 27 Apr 2023 13:47:57 +0200 Subject: [PATCH 062/101] Properly consume disconnected state --- .../java/no/nordicsemi/android/bps/view/BPSScreen.kt | 2 +- .../java/no/nordicsemi/android/cgms/view/CGMScreen.kt | 5 ----- .../java/no/nordicsemi/android/csc/view/CSCScreen.kt | 2 +- .../no/nordicsemi/android/gls/main/view/GLSScreen.kt | 2 +- .../java/no/nordicsemi/android/hrs/view/HRSScreen.kt | 2 +- .../nordicsemi/android/hts/repository/HTSRepository.kt | 9 ++++++--- .../no/nordicsemi/android/hts/repository/HTSService.kt | 5 +++++ .../java/no/nordicsemi/android/hts/view/HTSScreen.kt | 3 +-- .../no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt | 2 +- .../java/no/nordicsemi/android/rscs/view/RSCSScreen.kt | 2 +- 10 files changed, 18 insertions(+), 16 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 03d19b54..8fd0a5d9 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -83,7 +83,7 @@ fun BPSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.result.connectionState.status) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> BPSContentView(state.result) { viewModel.onEvent(it) } 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 3fb0e623..2e02bba0 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 @@ -40,18 +40,13 @@ import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier -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 import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerBackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 7c454235..8ae5d0a3 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -86,7 +86,7 @@ fun CSCScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> CSCContentView(state) { viewModel.onEvent(it) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index 8e105581..7bda808b 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -84,7 +84,7 @@ fun GLSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.glsServiceData.connectionState.status) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> GLSContentView(state.glsServiceData) { viewModel.onEvent(it) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index 3219714d..d04d177b 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -83,7 +83,7 @@ fun HRSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> HRSContentView(state) { viewModel.onEvent(it) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 4e4224fe..78b9836a 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -94,9 +94,12 @@ class HTSRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { - logger = null - _data.value = HTSServiceData() + fun disconnect() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + fun clear() { + logger = null + _data.value = HTSServiceData() + } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 349620a2..cf6e783f 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -144,4 +144,9 @@ internal class HTSService : NotificationService() { private fun disconnect() { client.disconnect() } + + override fun onDestroy() { + super.onDestroy() + repository.clear() + } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index be224b0e..df6535ed 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -44,7 +44,6 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView -import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.hts.R import no.nordicsemi.android.hts.viewmodel.HTSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState @@ -84,7 +83,7 @@ fun HTSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> HTSContentView(state) { viewModel.onEvent(it) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index fbc760f1..e1f854e2 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -114,7 +114,7 @@ internal class HTSViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.disconnect() navigationManager.navigateUp() } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index 06e9c1a0..d25e3057 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -83,7 +83,7 @@ fun RSCSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> RSCSContentView(state) { viewModel.onEvent(it) } From 38438e4af2256e01e4c2e774545fc5e58768c938 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 27 Apr 2023 14:13:35 +0200 Subject: [PATCH 063/101] Change approach for service, viewmodel state management for HTS profile: --- .../android/hts/repository/HTSRepository.kt | 19 ++++++++++++++++++- .../android/hts/repository/HTSService.kt | 4 +++- .../nordicsemi/android/hts/view/HTSScreen.kt | 3 +++ .../android/hts/viewmodel/HTSViewModel.kt | 7 +++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 78b9836a..629a782a 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -70,10 +70,27 @@ class HTSRepository @Inject constructor( val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } + private var isOnScreen = false + private var isServiceRunning = false + fun launch(device: ServerDevice) { serviceManager.startService(HTSService::class.java, device) } + fun setOnScreen(isOnScreen: Boolean) { + this.isOnScreen = isOnScreen + + if (shouldClean()) clean() + } + + fun setServiceRunning(serviceRunning: Boolean) { + this.isServiceRunning = serviceRunning + + if (shouldClean()) clean() + } + + private fun shouldClean() = !isOnScreen && !isServiceRunning + internal fun setTemperatureUnit(temperatureUnit: TemperatureUnit) { _data.value = _data.value.copy(temperatureUnit = temperatureUnit) } @@ -98,7 +115,7 @@ class HTSRepository @Inject constructor( _stopEvent.tryEmit(DisconnectAndStopEvent()) } - fun clear() { + private fun clean() { logger = null _data.value = HTSServiceData() } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index cf6e783f..5d0c27e4 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -79,6 +79,8 @@ internal class HTSService : NotificationService() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) + repository.setServiceRunning(true) + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! startGattClient(device) @@ -147,6 +149,6 @@ internal class HTSService : NotificationService() { override fun onDestroy() { super.onDestroy() - repository.clear() + repository.setServiceRunning(false) } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index df6535ed..2e1f9969 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.hts.view +import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -57,6 +58,8 @@ fun HTSScreen() { val state = viewModel.state.collectAsState().value val deviceName = viewModel.deviceName.collectAsState().value + Log.d("AAATESTAAA", "State: $state") + val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index e1f854e2..64d4abb1 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -71,6 +71,8 @@ internal class HTSViewModel @Inject constructor( val deviceName = _deviceName.asStateFlow() init { + repository.setOnScreen(true) + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -121,4 +123,9 @@ internal class HTSViewModel @Inject constructor( private fun onTemperatureUnitSelected(event: OnTemperatureUnitSelected) { repository.setTemperatureUnit(event.value) } + + override fun onCleared() { + super.onCleared() + repository.setOnScreen(false) + } } From 24db1bcc2b716e2e614a8324fc22ce0687719433 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 27 Apr 2023 16:00:31 +0200 Subject: [PATCH 064/101] Change approach for service & viewmodel state managment for all profiles --- .../nordicsemi/android/bps/view/BPSScreen.kt | 4 +-- .../android/bps/viewmodel/BPSViewModel.kt | 8 ----- .../android/cgms/data/CGMServiceData.kt | 3 +- .../android/cgms/repository/CGMRepository.kt | 29 +++++++++++++----- .../android/cgms/repository/CGMService.kt | 25 ++++++---------- .../nordicsemi/android/cgms/view/CGMScreen.kt | 7 ++--- .../android/cgms/viewmodel/CGMViewModel.kt | 15 +++++++++- .../android/csc/repository/CSCRepository.kt | 11 +------ .../android/csc/repository/CSCService.kt | 19 ++---------- .../nordicsemi/android/csc/view/CSCScreen.kt | 2 -- .../android/csc/viewmodel/CSCViewModel.kt | 2 +- .../android/gls/data/GLSServiceData.kt | 3 +- .../gls/main/viewmodel/GLSViewModel.kt | 14 ++------- .../android/hrs/data/HRSServiceData.kt | 3 +- .../android/hrs/service/HRSRepository.kt | 28 +++++++++++++---- .../android/hrs/service/HRSService.kt | 21 +++++-------- .../nordicsemi/android/hrs/view/HRSScreen.kt | 7 ++--- .../android/hrs/viewmodel/HRSViewModel.kt | 15 +++++++++- .../android/hts/repository/HTSRepository.kt | 8 ++--- .../android/hts/repository/HTSService.kt | 7 ++--- .../nordicsemi/android/hts/view/HTSScreen.kt | 2 -- .../android/prx/data/PRXServiceData.kt | 3 +- .../android/prx/repository/PRXRepository.kt | 29 ++++++++++++++---- .../android/prx/repository/PRXService.kt | 16 +++++----- .../nordicsemi/android/prx/view/PRXScreen.kt | 7 ++--- .../android/prx/viewmodel/PRXViewModel.kt | 10 ++++++- .../android/rscs/data/RSCSServiceData.kt | 3 +- .../android/rscs/repository/RSCSRepository.kt | 30 ++++++++++++++----- .../android/rscs/repository/RSCSService.kt | 21 +++++-------- .../android/rscs/view/RSCSScreen.kt | 7 ++--- .../android/rscs/viewmodel/RSCSViewModel.kt | 9 +++++- .../android/uart/data/UARTServiceData.kt | 3 +- .../android/uart/repository/UARTRepository.kt | 28 +++++++++++++---- .../android/uart/repository/UARTService.kt | 21 +++++-------- .../android/uart/view/UARTScreen.kt | 6 ++-- .../nordicsemi/android/uart/view/UARTState.kt | 3 +- .../android/uart/viewmodel/UARTViewModel.kt | 16 ++++++++-- 37 files changed, 244 insertions(+), 201 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 8fd0a5d9..dbb647b8 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -77,9 +77,7 @@ fun BPSScreen() { .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - if (state.deviceName == null) { - DeviceConnectingView { NavigateUpButton(navigateUp) } - } else when (state.result.connectionState?.state) { + when (state.result.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index a6d3b454..7eb8da48 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -128,11 +128,9 @@ internal class BPSViewModel @Inject constructor( .onEach { onDataUpdate(it) } .onEach { stopIfDisconnected(it.state) } .onEach { logAnalytics(it.state) } - .onEach { unlockUiIfDisconnected(it, device) } .launchIn(viewModelScope) if (!client.isConnected) { - _state.value = _state.value.copy(deviceName = device.name) return@launch } @@ -142,12 +140,6 @@ internal class BPSViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { - _state.value = _state.value.copy(deviceName = device.name) - } - } - private suspend fun configureGatt(services: BleGattServices) { val bpsService = services.findService(BPS_SERVICE_UUID)!! val bpmCharacteristic = bpsService.findCharacteristic(BPM_CHARACTERISTIC_UUID)!! 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 2307c447..3cf495f6 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,8 +8,7 @@ internal data class CGMServiceData( val records: List = emptyList(), val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, - val requestStatus: RequestStatus = RequestStatus.IDLE, - val deviceName: String? = null + val requestStatus: RequestStatus = RequestStatus.IDLE ) 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 24d76fb3..14715bef 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 @@ -32,7 +32,6 @@ package no.nordicsemi.android.cgms.repository import android.content.Context -import android.util.Log import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow @@ -74,6 +73,23 @@ class CGMRepository @Inject constructor( val hasRecords = data.value.records.isNotEmpty() val highestSequenceNumber = data.value.records.maxOfOrNull { it.sequenceNumber } ?: -1 + private var isOnScreen = false + private var isServiceRunning = false + + fun setOnScreen(isOnScreen: Boolean) { + this.isOnScreen = isOnScreen + + if (shouldClean()) clean() + } + + fun setServiceRunning(serviceRunning: Boolean) { + this.isServiceRunning = serviceRunning + + if (shouldClean()) clean() + } + + private fun shouldClean() = !isOnScreen && !isServiceRunning + fun launch(device: ServerDevice) { serviceManager.startService(CGMService::class.java, device) } @@ -102,16 +118,15 @@ class CGMRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun onInitComplete(device: ServerDevice) { - _data.value = _data.value.copy(deviceName = device.name) - } - fun clear() { _data.value = _data.value.copy(records = emptyList()) } - fun release() { - _data.value = CGMServiceData() + fun disconnect() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + fun clean() { + _data.value = CGMServiceData() + } } 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 e43cf74f..03cb77c4 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 @@ -104,11 +104,11 @@ internal class CGMService : NotificationService() { private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic - private var hasBeenInitialized: Boolean = false - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) + repository.setServiceRunning(true) + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! startGattClient(device) @@ -146,22 +146,19 @@ internal class CGMService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } - .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { - hasBeenInitialized = true - repository.onInitComplete(device) return@launch } client.discoverServices() .filterNotNull() - .onEach { configureGatt(it, device) } + .onEach { configureGatt(it) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + private suspend fun configureGatt(services: BleGattServices) { val cgmService = services.findService(CGMS_SERVICE_UUID)!! val statusCharacteristic = cgmService.findCharacteristic(CGM_STATUS_UUID)!! val featureCharacteristic = cgmService.findCharacteristic(CGM_FEATURE_UUID)!! @@ -232,9 +229,6 @@ internal class CGMService : NotificationService() { if (sessionStartTime == 0L) { opsControlPointCharacteristic.write(CGMSpecificOpsControlPointData.startSession(secured).value!!) } - - hasBeenInitialized = true - repository.onInitComplete(device) } private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) = lifecycleScope.launch { @@ -317,13 +311,12 @@ internal class CGMService : NotificationService() { } } - private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { - repository.onInitComplete(device) - } - } - private fun disconnect() { client.disconnect() } + + override fun onDestroy() { + super.onDestroy() + repository.setServiceRunning(false) + } } 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 2e02bba0..994b42e8 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 @@ -55,13 +55,14 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun CGMScreen() { val viewModel: CGMViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value + val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( topBar = { ProfileAppBar( - deviceName = state.deviceName, + deviceName = deviceName, connectionState = state.connectionState, title = R.string.cgms_title, navigateUp = navigateUp, @@ -76,9 +77,7 @@ fun CGMScreen() { .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - if (state.deviceName == null) { - DeviceConnectingView { NavigateUpButton(navigateUp) } - } else when (state.connectionState?.state) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } 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 abe520d8..b5a93b59 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 @@ -35,6 +35,8 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -66,7 +68,12 @@ internal class CGMViewModel @Inject constructor( val state = repository.data + private val _deviceName = MutableStateFlow(null) + val deviceName = _deviceName.asStateFlow() + init { + repository.setOnScreen(true) + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -105,6 +112,7 @@ internal class CGMViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { + _deviceName.value = device.name repository.launch(device) } @@ -113,6 +121,11 @@ internal class CGMViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.disconnect() + } + + override fun onCleared() { + super.onCleared() + repository.setOnScreen(false) } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 94544f15..d14b1c64 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -77,14 +77,6 @@ class CSCRepository @Inject constructor( serviceManager.startService(CSCService::class.java, device) } - fun onInitComplete(device: ServerDevice) { - Log.d("AAATESTAAA", "onInitComplete: ${data.value}") - if (_data.value.deviceName == null) { - Log.d("AAATESTAAA", "AAA") - _data.value = _data.value.copy(deviceName = device.name) - } - } - internal fun setSpeedUnit(speedUnit: SpeedUnit) { _data.value = _data.value.copy(speedUnit = speedUnit) } @@ -109,8 +101,7 @@ class CSCRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { - Log.d("AAATESTAAA", "release: ${data.value}") + fun disconnect() { _data.value = CSCServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 5e21dd1d..de72c23e 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -73,8 +73,6 @@ internal class CSCService : NotificationService() { private lateinit var client: BleGattClient - private var hasBeenInitialized: Boolean = false - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -102,22 +100,19 @@ internal class CSCService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } - .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { - hasBeenInitialized = true - repository.onInitComplete(device) return@launch } client.discoverServices() .filterNotNull() - .onEach { configureGatt(it, device) } + .onEach { configureGatt(it) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + private suspend fun configureGatt(services: BleGattServices) { val cscService = services.findService(CSC_SERVICE_UUID)!! val cscMeasurementCharacteristic = cscService.findCharacteristic(CSC_MEASUREMENT_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! @@ -133,9 +128,6 @@ internal class CSCService : NotificationService() { .mapNotNull { cscDataParser.parse(it, repository.wheelSize.value) } .onEach { repository.onCSCDataChanged(it) } .launchIn(lifecycleScope) - - hasBeenInitialized = true - repository.onInitComplete(device) } private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { @@ -144,13 +136,6 @@ internal class CSCService : NotificationService() { } } - private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { - hasBeenInitialized = true - repository.onInitComplete(device) - } - } - private fun disconnect() { client.disconnect() } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 8ae5d0a3..662b2455 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -58,8 +58,6 @@ fun CSCScreen() { val viewModel: CSCViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - Log.d("AAATESTAAA", "State: ${state}") - val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index 47e61ed5..743647f4 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -115,7 +115,7 @@ internal class CSCViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.disconnect() navigationManager.navigateUp() } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt index a5077951..8a75c921 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/data/GLSServiceData.kt @@ -31,10 +31,9 @@ package no.nordicsemi.android.gls.data -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus -import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus internal data class GLSServiceData( diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index c10be870..0cb52598 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -163,17 +163,15 @@ internal class GLSViewModel @Inject constructor( client = device.connect(context, logger = logger) + client.waitForBonding() + client.connectionStateWithStatus .filterNotNull() .onEach { _state.value = _state.value.copyWithNewConnectionState(it) } .onEach { logAnalytics(it) } - .onEach { unlockUiIfDisconnected(it, device) } .launchIn(viewModelScope) - client.waitForBonding() - if (!client.isConnected) { - _state.value = _state.value.copy(deviceName = device.name) return@launch } @@ -189,12 +187,6 @@ internal class GLSViewModel @Inject constructor( } } - private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { - _state.value = _state.value.copy(deviceName = device.name) - } - } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val glsService = services.findService(GLS_SERVICE_UUID)!! glucoseMeasurementCharacteristic = glsService.findCharacteristic(GM_CHARACTERISTIC)!! @@ -221,8 +213,6 @@ internal class GLSViewModel @Inject constructor( .mapNotNull { RecordAccessControlPointParser.parse(it) } .onEach { onAccessControlPointDataReceived(it) } .launchIn(viewModelScope) - - _state.value = _state.value.copy(deviceName = device.name) //prevents UI from appearing before BLE connection is set up } private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) = viewModelScope.launch { diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt index 89f5cebf..fc9d7fad 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt @@ -40,8 +40,7 @@ internal data class HRSServiceData( val bodySensorLocation: Int? = null, val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, - val zoomIn: Boolean = false, - val deviceName: String? = null + val zoomIn: Boolean = false ) { val heartRates = data.map { it.heartRate } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 4de965a0..172de950 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -69,12 +69,25 @@ class HRSRepository @Inject constructor( val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } - fun launch(device: ServerDevice) { - serviceManager.startService(HRSService::class.java, device) + private var isOnScreen = false + private var isServiceRunning = false + + fun setOnScreen(isOnScreen: Boolean) { + this.isOnScreen = isOnScreen + + if (shouldClean()) clean() } - fun onInitComplete(device: ServerDevice) { - _data.value = _data.value.copy(deviceName = device.name) + fun setServiceRunning(serviceRunning: Boolean) { + this.isServiceRunning = serviceRunning + + if (shouldClean()) clean() + } + + private fun shouldClean() = !isOnScreen && !isServiceRunning + + fun launch(device: ServerDevice) { + serviceManager.startService(HRSService::class.java, device) } fun switchZoomIn() { @@ -101,8 +114,11 @@ class HRSRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { - _data.value = HRSServiceData() + fun disconnect() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + private fun clean() { + _data.value = HRSServiceData() + } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 18166de5..71cccdc0 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -75,11 +75,11 @@ internal class HRSService : NotificationService() { private lateinit var client: BleGattClient - private var hasBeenInitialized: Boolean = false - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) + repository.setServiceRunning(true) + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! startGattClient(device) @@ -104,14 +104,11 @@ internal class HRSService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } - .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) client.waitForBonding() if (!client.isConnected) { - hasBeenInitialized = true - repository.onInitComplete(device) return@launch } @@ -140,9 +137,6 @@ internal class HRSService : NotificationService() { .mapNotNull { HRSDataParser.parse(it) } .onEach { repository.onHRSDataChanged(it) } .launchIn(lifecycleScope) - - hasBeenInitialized = true - repository.onInitComplete(device) } private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { @@ -151,13 +145,12 @@ internal class HRSService : NotificationService() { } } - private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { - repository.onInitComplete(device) - } - } - private fun disconnect() { client.disconnect() } + + override fun onDestroy() { + super.onDestroy() + repository.setServiceRunning(false) + } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index d04d177b..d6bef795 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -56,13 +56,14 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun HRSScreen() { val viewModel: HRSViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value + val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUpEvent) } Scaffold( topBar = { ProfileAppBar( - deviceName = state.deviceName, + deviceName = deviceName, connectionState = state.connectionState, title = R.string.hrs_title, navigateUp = navigateUp, @@ -77,9 +78,7 @@ fun HRSScreen() { .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - if (state.deviceName == null) { - DeviceConnectingView { NavigateUpButton(navigateUp) } - } else when (state.connectionState?.state) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index 3168334b..9ddfcffa 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -35,6 +35,8 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -65,7 +67,12 @@ internal class HRSViewModel @Inject constructor( val state = repository.data + private val _deviceName = MutableStateFlow(null) + val deviceName = _deviceName.asStateFlow() + init { + repository.setOnScreen(true) + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -95,6 +102,7 @@ internal class HRSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { + _deviceName.value = device.name repository.launch(device) } @@ -112,7 +120,12 @@ internal class HRSViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.disconnect() navigationManager.navigateUp() } + + override fun onCleared() { + super.onCleared() + repository.setOnScreen(false) + } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 629a782a..93925047 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -73,10 +73,6 @@ class HTSRepository @Inject constructor( private var isOnScreen = false private var isServiceRunning = false - fun launch(device: ServerDevice) { - serviceManager.startService(HTSService::class.java, device) - } - fun setOnScreen(isOnScreen: Boolean) { this.isOnScreen = isOnScreen @@ -91,6 +87,10 @@ class HTSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning + fun launch(device: ServerDevice) { + serviceManager.startService(HTSService::class.java, device) + } + internal fun setTemperatureUnit(temperatureUnit: TemperatureUnit) { _data.value = _data.value.copy(temperatureUnit = temperatureUnit) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 5d0c27e4..3c503085 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -74,8 +74,6 @@ internal class HTSService : NotificationService() { private lateinit var client: BleGattClient - private var hasBeenInitialized: Boolean = false - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -104,7 +102,6 @@ internal class HTSService : NotificationService() { .launchIn(lifecycleScope) client.connectionStateWithStatus - .onEach { Log.d("AAATESTAAA", "Connection state: $it") } .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } @@ -116,11 +113,11 @@ internal class HTSService : NotificationService() { client.discoverServices() .filterNotNull() - .onEach { configureGatt(it, device) } + .onEach { configureGatt(it) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + private suspend fun configureGatt(services: BleGattServices) { val htsService = services.findService(HTS_SERVICE_UUID)!! val htsMeasurementCharacteristic = htsService.findCharacteristic(HTS_MEASUREMENT_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 2e1f9969..42705f31 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -58,8 +58,6 @@ fun HTSScreen() { val state = viewModel.state.collectAsState().value val deviceName = viewModel.deviceName.collectAsState().value - Log.d("AAATESTAAA", "State: $state") - val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt index 48e2a373..90ff0f97 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -11,8 +11,7 @@ data class PRXServiceData( val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, val connectionStatus: BleGattConnectionStatus? = null, - val isRemoteAlarm: Boolean = false, - val deviceName: String? = null + val isRemoteAlarm: Boolean = false ) { val isLinkLossDisconnected = connectionStatus?.isLinkLoss ?: false diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 183a53af..d0eab65b 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -70,12 +70,26 @@ class PRXRepository @Inject internal constructor( val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } - fun launch(device: ServerDevice) { - serviceManager.startService(PRXService::class.java, device) + private var isOnScreen = false + private var isServiceRunning = false + + fun setOnScreen(isOnScreen: Boolean) { + this.isOnScreen = isOnScreen + + if (shouldClean()) clean() } - fun onInitComplete(device: ServerDevice) { - _data.value = _data.value.copy(deviceName = device.name) + fun setServiceRunning(serviceRunning: Boolean) { + this.isServiceRunning = serviceRunning + + if (shouldClean()) clean() + } + + private fun shouldClean() = !isOnScreen && !isServiceRunning + + + fun launch(device: ServerDevice) { + serviceManager.startService(PRXService::class.java, device) } fun onConnectionStateChanged(connection: GattConnectionStateWithStatus) { @@ -106,9 +120,12 @@ class PRXRepository @Inject internal constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { - _data.value = PRXServiceData() + fun disconnect() { _remoteAlarmLevel.tryEmit(AlarmLevel.NONE) _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + private fun clean() { + _data.value = PRXServiceData() + } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 96f668d3..062a447f 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -89,11 +89,11 @@ internal class PRXService : NotificationService() { private lateinit var alertLevelCharacteristic: BleGattCharacteristic - private var hasBeenInitialized: Boolean = false - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) + repository.setServiceRunning(true) + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! startServer(device) @@ -175,8 +175,6 @@ internal class PRXService : NotificationService() { .launchIn(lifecycleScope) if (!client.isConnected) { - hasBeenInitialized = true - repository.onInitComplete(device) return@launch } @@ -204,9 +202,6 @@ internal class PRXService : NotificationService() { .launchIn(lifecycleScope) linkLossCharacteristic.write(AlertLevelInputParser.parse(AlarmLevel.HIGH)) - - hasBeenInitialized = true - repository.onInitComplete(device) } private suspend fun writeAlertLevel(alarmLevel: AlarmLevel) { @@ -217,7 +212,7 @@ internal class PRXService : NotificationService() { private fun stopIfDisconnected(connectionState: GattConnectionState, connectionStatus: BleGattConnectionStatus) { if (connectionState == GattConnectionState.STATE_DISCONNECTED && !connectionStatus.isLinkLoss) { server.stopServer() - repository.release() + repository.disconnect() stopSelf() } } @@ -226,4 +221,9 @@ internal class PRXService : NotificationService() { client.disconnect() server.stopServer() } + + override fun onDestroy() { + super.onDestroy() + repository.setServiceRunning(false) + } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index 64140408..ab406789 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -56,13 +56,14 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun PRXScreen() { val viewModel: PRXViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value + val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUpEvent) } Scaffold( topBar = { ProfileAppBar( - deviceName = state.deviceName, + deviceName = deviceName, connectionState = state.connectionState, title = R.string.prx_title, navigateUp = navigateUp, @@ -77,9 +78,7 @@ fun PRXScreen() { .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - if (state.deviceName == null) { - DeviceConnectingView { NavigateUpButton(navigateUp) } - } else when (state.connectionState?.state) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index 53e86ce7..85fdf104 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -35,6 +35,8 @@ import android.os.ParcelUuid 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.distinctUntilChanged import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn @@ -71,7 +73,12 @@ internal class PRXViewModel @Inject constructor( val state = repository.data + private val _deviceName = MutableStateFlow(null) + val deviceName = _deviceName.asStateFlow() + init { + repository.setOnScreen(true) + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -123,11 +130,12 @@ internal class PRXViewModel @Inject constructor( private fun disconnect() { alarmHandler.pauseAlarm() navigationManager.navigateUp() - repository.release() + repository.disconnect() } override fun onCleared() { super.onCleared() alarmHandler.pauseAlarm() + repository.setOnScreen(false) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt index 5ffb23b6..c5fbfaa3 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt @@ -41,8 +41,7 @@ import no.nordicsemi.android.rscs.R internal data class RSCSServiceData( val data: RSCSData = RSCSData(), val batteryLevel: Int? = null, - val connectionState: GattConnectionStateWithStatus? = null, - val deviceName: String? = null + val connectionState: GattConnectionStateWithStatus? = null ) { @Composable fun displayActivity(): String { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 10bc3004..6f6fdbc2 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -69,12 +69,25 @@ class RSCSRepository @Inject constructor( val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } - fun launch(device: ServerDevice) { - serviceManager.startService(RSCSService::class.java, device) + private var isOnScreen = false + private var isServiceRunning = false + + fun setOnScreen(isOnScreen: Boolean) { + this.isOnScreen = isOnScreen + + if (shouldClean()) clean() } - fun onInitComplete(device: ServerDevice) { - _data.value = _data.value.copy(deviceName = device.name) + fun setServiceRunning(serviceRunning: Boolean) { + this.isServiceRunning = serviceRunning + + if (shouldClean()) clean() + } + + private fun shouldClean() = !isOnScreen && !isServiceRunning + + fun launch(device: ServerDevice) { + serviceManager.startService(RSCSService::class.java, device) } fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { @@ -93,9 +106,12 @@ class RSCSRepository @Inject constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } - fun release() { - logger = null - _data.value = RSCSServiceData() + fun disconnect() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + private fun clean() { + logger = null + _data.value = RSCSServiceData() + } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index 0056160d..17bae635 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -73,11 +73,11 @@ internal class RSCSService : NotificationService() { private lateinit var client: BleGattClient - private var hasBeenInitialized: Boolean = false - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) + repository.setServiceRunning(true) + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! startGattClient(device) @@ -102,12 +102,9 @@ internal class RSCSService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } - .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { - hasBeenInitialized = true - repository.onInitComplete(device) return@launch } @@ -132,9 +129,6 @@ internal class RSCSService : NotificationService() { .mapNotNull { RSCSDataParser.parse(it) } .onEach { repository.onRSCSDataChanged(it) } .launchIn(lifecycleScope) - - hasBeenInitialized = true - repository.onInitComplete(device) } private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { @@ -143,13 +137,12 @@ internal class RSCSService : NotificationService() { } } - private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { - repository.onInitComplete(device) - } - } - private fun disconnect() { client.disconnect() } + + override fun onDestroy() { + super.onDestroy() + repository.setServiceRunning(false) + } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index d25e3057..63ccf344 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -56,13 +56,14 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun RSCSScreen() { val viewModel: RSCSViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value + val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUpEvent) } Scaffold( topBar = { ProfileAppBar( - deviceName = state.deviceName, + deviceName = deviceName, connectionState = state.connectionState, title = R.string.rscs_title, navigateUp = navigateUp, @@ -77,9 +78,7 @@ fun RSCSScreen() { .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - if (state.deviceName == null) { - DeviceConnectingView { NavigateUpButton(navigateUp) } - } else when (state.connectionState?.state) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 57d912ac..46b6e379 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -35,6 +35,8 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -64,6 +66,10 @@ internal class RSCSViewModel @Inject constructor( val state = repository.data + private val _deviceName = MutableStateFlow(null) + val deviceName = _deviceName.asStateFlow() + + init { viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { @@ -94,6 +100,7 @@ internal class RSCSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { + _deviceName.value = device.name repository.launch(device) } @@ -106,7 +113,7 @@ internal class RSCSViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.disconnect() navigationManager.navigateUp() } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt index 51df55e0..9fd18690 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt @@ -37,8 +37,7 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus internal data class UARTServiceData( val messages: List = emptyList(), val connectionState: GattConnectionStateWithStatus? = null, - val batteryLevel: Int? = null, - val deviceName: String? = null + val batteryLevel: Int? = null ) { val displayMessages = messages diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index abcba7a8..979d9603 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -77,6 +77,23 @@ class UARTRepository @Inject internal constructor( val lastConfigurationName = configurationDataSource.lastConfigurationName + private var isOnScreen = false + private var isServiceRunning = false + + fun setOnScreen(isOnScreen: Boolean) { + this.isOnScreen = isOnScreen + + if (shouldClean()) clean() + } + + fun setServiceRunning(serviceRunning: Boolean) { + this.isServiceRunning = serviceRunning + + if (shouldClean()) clean() + } + + private fun shouldClean() = !isOnScreen && !isServiceRunning + fun launch(device: ServerDevice) { serviceManager.startService(UARTService::class.java, device) } @@ -97,10 +114,6 @@ class UARTRepository @Inject internal constructor( _data.value = _data.value.copy(messages = _data.value.messages + UARTRecord(value, UARTRecordType.INPUT)) } - fun onInitComplete(device: ServerDevice) { - _data.value = _data.value.copy(deviceName = device.name) - } - fun sendText(text: String, newLineChar: MacroEol) { _command.tryEmit(text.parseWithNewLineChar(newLineChar)) } @@ -124,8 +137,11 @@ class UARTRepository @Inject internal constructor( configurationDataSource.saveConfigurationName(name) } - fun release() { - _data.value = UARTServiceData() + fun disconnect() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + private fun clean() { + _data.value = UARTServiceData() + } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index ae63213c..b21958a6 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -78,11 +78,11 @@ internal class UARTService : NotificationService() { private lateinit var client: BleGattClient - private var hasBeenInitialized: Boolean = false - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) + repository.setServiceRunning(true) + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! startGattClient(device) @@ -109,12 +109,9 @@ internal class UARTService : NotificationService() { .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() .onEach { stopIfDisconnected(it) } - .onEach { unlockUiIfDisconnected(it, device) } .launchIn(lifecycleScope) if (!client.isConnected) { - hasBeenInitialized = true - repository.onInitComplete(device) return@launch } @@ -146,9 +143,6 @@ internal class UARTService : NotificationService() { .onEach { repository.onNewMessageSent(it) } .onEach { logger.log(10, "Sent: $it") } .launchIn(lifecycleScope) - - hasBeenInitialized = true - repository.onInitComplete(device) } private fun getWriteType(characteristic: BleGattCharacteristic): BleWriteType { @@ -165,13 +159,12 @@ internal class UARTService : NotificationService() { } } - private fun unlockUiIfDisconnected(connectionState: GattConnectionStateWithStatus, device: ServerDevice) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED && !hasBeenInitialized) { - repository.onInitComplete(device) - } - } - private fun disconnect() { client.disconnect() } + + override fun onDestroy() { + super.onDestroy() + repository.setServiceRunning(false) + } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index 6de05a50..33fb4c2a 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -68,7 +68,7 @@ fun UARTScreen() { Scaffold( topBar = { ProfileAppBar( - deviceName = state.uartManagerState.deviceName, + deviceName = state.deviceName, connectionState = state.uartManagerState.connectionState, title = R.string.uart_title, navigateUp = navigateUp, @@ -80,9 +80,7 @@ fun UARTScreen() { Column( modifier = Modifier.padding(it) ) { - if (state.uartManagerState.deviceName == null) { - DeviceConnectingView { NavigateUpButton(navigateUp) } - } else when (state.uartManagerState.connectionState?.state) { + when (state.uartManagerState.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt index ff9fe7ad..4756d97f 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt @@ -41,7 +41,8 @@ internal data class UARTViewState( val isConfigurationEdited: Boolean = false, val configurations: List = emptyList(), val uartManagerState: UARTServiceData = UARTServiceData(), - val isInputVisible: Boolean = true + val isInputVisible: Boolean = true, + val deviceName: String? = null ) { val showEditDialog: Boolean = editedPosition != null diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index 30e79922..c29a1a74 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -91,6 +91,8 @@ internal class UARTViewModel @Inject constructor( val state = _state.asStateFlow() init { + repository.setOnScreen(true) + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -127,10 +129,15 @@ internal class UARTViewModel @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) { + _state.value = _state.value.copy(deviceName = device.name) + repository.launch(device) + } + fun onEvent(event: UARTViewEvent) { when (event) { is OnCreateMacro -> addNewMacro(event.macro) @@ -233,7 +240,12 @@ internal class UARTViewModel @Inject constructor( } private fun disconnect() { - repository.release() + repository.disconnect() navigationManager.navigateUp() } + + override fun onCleared() { + super.onCleared() + repository.setOnScreen(false) + } } From 9779bec1c15e3e0d9d88eef773b93e1738661539 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 27 Apr 2023 16:11:38 +0200 Subject: [PATCH 065/101] Optimize imports --- .../nordicsemi/android/toolbox/scanner/ScannerDestination.kt | 1 - lib_utils/src/main/java/no/nordicsemi/android/utils/Ext.kt | 1 - .../src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt | 1 - .../java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt | 2 +- .../main/java/no/nordicsemi/android/cgms/view/CGMMapper.kt | 4 ++-- .../no/nordicsemi/android/csc/repository/CSCRepository.kt | 1 - .../main/java/no/nordicsemi/android/csc/view/CSCMappers.kt | 2 +- .../src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt | 2 -- .../src/main/java/no/nordicsemi/android/gls/GLSDestination.kt | 2 +- .../android/gls/details/view/GLSDetailsContentView.kt | 2 +- .../java/no/nordicsemi/android/gls/main/view/GLSScreen.kt | 2 -- .../java/no/nordicsemi/android/gls/main/view/GLSViewState.kt | 1 - .../no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt | 2 -- .../java/no/nordicsemi/android/hrs/data/HRSServiceData.kt | 1 - .../src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt | 1 - .../java/no/nordicsemi/android/hts/data/HTSServiceData.kt | 1 - .../java/no/nordicsemi/android/hts/repository/HTSService.kt | 1 - .../src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt | 1 - .../java/no/nordicsemi/android/prx/data/PRXServiceData.kt | 1 - .../src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt | 1 - .../java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt | 1 - .../main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt | 1 - .../java/no/nordicsemi/android/uart/data/UARTServiceData.kt | 1 - .../java/no/nordicsemi/android/uart/repository/UARTService.kt | 1 - .../java/no/nordicsemi/android/uart/view/OutputSection.kt | 2 +- .../main/java/no/nordicsemi/android/uart/view/UARTState.kt | 2 +- 26 files changed, 8 insertions(+), 30 deletions(-) diff --git a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt index d694e116..8e86d360 100644 --- a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt +++ b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt @@ -9,7 +9,6 @@ import no.nordicsemi.android.common.ui.scanner.DeviceSelected import no.nordicsemi.android.common.ui.scanner.ScannerScreen import no.nordicsemi.android.common.ui.scanner.ScanningCancelled import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import java.util.* val ScannerDestinationId = createDestination("uiscanner-destination") 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 e241bf63..d5bac361 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 @@ -36,7 +36,6 @@ import android.content.Context import android.util.Log import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index dbb647b8..2abb728c 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -46,7 +46,6 @@ import no.nordicsemi.android.bps.R import no.nordicsemi.android.bps.viewmodel.BPSViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView -import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 7eb8da48..33ec55a6 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -68,7 +68,7 @@ import no.nordicsemi.android.kotlin.ble.profile.bps.data.BloodPressureMeasuremen import no.nordicsemi.android.kotlin.ble.profile.bps.data.IntermediateCuffPressureData import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.ui.view.StringConst -import java.util.* +import java.util.UUID import javax.inject.Inject val BPS_SERVICE_UUID: UUID = UUID.fromString("00001810-0000-1000-8000-00805f9b34fb") diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMMapper.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMMapper.kt index f95c98af..aab7d8d7 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMMapper.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMMapper.kt @@ -35,9 +35,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import no.nordicsemi.android.cgms.R import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber -import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMRecord import java.text.SimpleDateFormat -import java.util.* +import java.util.Date +import java.util.Locale internal fun CGMRecordWithSequenceNumber.formattedTime(): String { val timeFormat = SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.US) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index d14b1c64..6091ab0e 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -32,7 +32,6 @@ package no.nordicsemi.android.csc.repository import android.content.Context -import android.util.Log import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt index e60e3f30..e87358f2 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt @@ -35,7 +35,7 @@ import no.nordicsemi.android.common.theme.view.RadioButtonItem import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData -import java.util.* +import java.util.Locale private const val DISPLAY_M_S = "m/s" private const val DISPLAY_KM_H = "km/h" diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 662b2455..59262b22 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -31,7 +31,6 @@ package no.nordicsemi.android.csc.view -import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -45,7 +44,6 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView -import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.viewmodel.CSCViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSDestination.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSDestination.kt index a39603e1..db69b290 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSDestination.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSDestination.kt @@ -34,8 +34,8 @@ package no.nordicsemi.android.gls import no.nordicsemi.android.common.navigation.createDestination import no.nordicsemi.android.common.navigation.defineDestination import no.nordicsemi.android.gls.details.view.GLSDetailsScreen -import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord internal val GlsDetailsDestinationId = createDestination, Unit>("gls-details-screen") diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt index e0ca33e2..6112db26 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsContentView.kt @@ -50,8 +50,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import no.nordicsemi.android.gls.R import no.nordicsemi.android.gls.main.view.toDisplayString -import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext +import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord import no.nordicsemi.android.ui.view.ScreenSection @Composable diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index 7bda808b..c75c99a3 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -31,7 +31,6 @@ package no.nordicsemi.android.gls.main.view -import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -45,7 +44,6 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView -import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.gls.R import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt index 2f04e457..fd157f73 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt @@ -32,7 +32,6 @@ package no.nordicsemi.android.gls.main.view import no.nordicsemi.android.gls.data.GLSServiceData -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 0cb52598..543364d1 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -34,12 +34,10 @@ package no.nordicsemi.android.gls.main.viewmodel import android.annotation.SuppressLint import android.content.Context import android.os.ParcelUuid -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterNotNull diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt index fc9d7fad..5cbde590 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt @@ -31,7 +31,6 @@ package no.nordicsemi.android.hrs.data -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index d6bef795..12ae54b4 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -44,7 +44,6 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView -import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.hrs.R import no.nordicsemi.android.hrs.viewmodel.HRSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt index 11fcc397..870f452e 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt @@ -32,7 +32,6 @@ package no.nordicsemi.android.hts.data import no.nordicsemi.android.hts.view.TemperatureUnit -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 3c503085..a4c3f8bb 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -33,7 +33,6 @@ package no.nordicsemi.android.hts.repository import android.annotation.SuppressLint import android.content.Intent -import android.util.Log import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.filterNotNull diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 42705f31..df6535ed 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -31,7 +31,6 @@ package no.nordicsemi.android.hts.view -import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt index 90ff0f97..16edefff 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -1,7 +1,6 @@ package no.nordicsemi.android.prx.data import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index ab406789..68fc6579 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -31,7 +31,6 @@ package no.nordicsemi.android.prx.view -import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt index c5fbfaa3..4d422e49 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt @@ -33,7 +33,6 @@ package no.nordicsemi.android.rscs.data import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.rscs.data.RSCSData import no.nordicsemi.android.rscs.R diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index 63ccf344..e0c73433 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -44,7 +44,6 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView -import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.rscs.R import no.nordicsemi.android.rscs.viewmodel.RSCSViewModel diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt index 9fd18690..33f120c3 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt @@ -31,7 +31,6 @@ package no.nordicsemi.android.uart.data -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus internal data class UARTServiceData( diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index b21958a6..32264bda 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -33,7 +33,6 @@ package no.nordicsemi.android.uart.repository import android.annotation.SuppressLint import android.content.Intent -import android.util.Log import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.filterNotNull diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/OutputSection.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/OutputSection.kt index 95d4da59..42145413 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/OutputSection.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/OutputSection.kt @@ -64,7 +64,7 @@ import no.nordicsemi.android.uart.data.UARTRecord import no.nordicsemi.android.uart.data.UARTRecordType import no.nordicsemi.android.ui.view.SectionTitle import java.text.SimpleDateFormat -import java.util.* +import java.util.Locale @Composable internal fun OutputSection(records: List, onEvent: (UARTViewEvent) -> Unit) { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt index 4756d97f..7e7f7631 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt @@ -32,8 +32,8 @@ package no.nordicsemi.android.uart.view import no.nordicsemi.android.uart.data.UARTConfiguration -import no.nordicsemi.android.uart.data.UARTServiceData import no.nordicsemi.android.uart.data.UARTMacro +import no.nordicsemi.android.uart.data.UARTServiceData internal data class UARTViewState( val editedPosition: Int? = null, From 6163e570f851d142c7054fe75c5bfc878e3df319 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 27 Apr 2023 16:23:34 +0200 Subject: [PATCH 066/101] Remove invalid condition --- .../main/java/no/nordicsemi/android/csc/view/CSCScreen.kt | 4 +--- .../java/no/nordicsemi/android/gls/main/view/GLSScreen.kt | 4 +--- .../main/java/no/nordicsemi/android/hts/view/HTSScreen.kt | 5 ++--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 59262b22..578b168b 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -76,9 +76,7 @@ fun CSCScreen() { .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - if (state.deviceName == null) { - DeviceConnectingView { NavigateUpButton(navigateUp) } - } else when (state.connectionState?.state) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index c75c99a3..b8ecd090 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -76,9 +76,7 @@ fun GLSScreen() { .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - if (state.deviceName == null) { - DeviceConnectingView { NavigateUpButton(navigateUp) } - } else when (state.glsServiceData.connectionState?.state) { + when (state.glsServiceData.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index df6535ed..1360a79c 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.hts.view +import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -77,9 +78,7 @@ fun HTSScreen() { .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - if (deviceName == null) { - DeviceConnectingView { NavigateUpButton(navigateUp) } - } else when (state.connectionState?.state) { + when (state.connectionState?.state) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, From 6c0acbfba935e6b768b2683dda4e5405a10b4c50 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 27 Apr 2023 16:38:42 +0200 Subject: [PATCH 067/101] Revert to keeping deviceName in Repository data to preserve reentering the screen --- .../java/no/nordicsemi/android/cgms/data/CGMServiceData.kt | 3 ++- .../no/nordicsemi/android/cgms/repository/CGMRepository.kt | 1 + .../main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt | 3 +-- .../no/nordicsemi/android/cgms/viewmodel/CGMViewModel.kt | 6 ------ .../no/nordicsemi/android/csc/repository/CSCRepository.kt | 1 + .../java/no/nordicsemi/android/hrs/data/HRSServiceData.kt | 3 ++- .../no/nordicsemi/android/hrs/service/HRSRepository.kt | 1 + .../main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt | 3 +-- .../no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt | 6 ------ .../java/no/nordicsemi/android/hts/data/HTSServiceData.kt | 3 ++- .../no/nordicsemi/android/hts/repository/HTSRepository.kt | 1 + .../main/java/no/nordicsemi/android/hts/view/HTSScreen.kt | 4 +--- .../no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt | 6 ------ .../java/no/nordicsemi/android/prx/data/PRXServiceData.kt | 3 ++- .../no/nordicsemi/android/prx/repository/PRXRepository.kt | 1 + .../main/java/no/nordicsemi/android/prx/view/PRXScreen.kt | 3 +-- .../no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt | 5 ----- .../no/nordicsemi/android/rscs/data/RSCSServiceData.kt | 3 ++- .../nordicsemi/android/rscs/repository/RSCSRepository.kt | 1 + .../java/no/nordicsemi/android/rscs/view/RSCSScreen.kt | 3 +-- .../no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt | 7 ------- .../no/nordicsemi/android/uart/data/UARTServiceData.kt | 3 ++- .../nordicsemi/android/uart/repository/UARTRepository.kt | 1 + .../java/no/nordicsemi/android/uart/view/UARTScreen.kt | 2 +- .../main/java/no/nordicsemi/android/uart/view/UARTState.kt | 3 +-- .../no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt | 1 - 26 files changed, 26 insertions(+), 51 deletions(-) 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 3cf495f6..2307c447 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: GattConnectionStateWithStatus? = 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 14715bef..44a3e917 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 @@ -91,6 +91,7 @@ class CGMRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning 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/view/CGMScreen.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/view/CGMScreen.kt index 994b42e8..121759ae 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 @@ -55,14 +55,13 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun CGMScreen() { val viewModel: CGMViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( topBar = { ProfileAppBar( - deviceName = deviceName, + deviceName = state.deviceName, connectionState = state.connectionState, title = R.string.cgms_title, navigateUp = navigateUp, 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 b5a93b59..a06eb650 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 @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -68,9 +66,6 @@ internal class CGMViewModel @Inject constructor( val state = repository.data - private val _deviceName = MutableStateFlow(null) - val deviceName = _deviceName.asStateFlow() - init { repository.setOnScreen(true) @@ -112,7 +107,6 @@ internal class CGMViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _deviceName.value = device.name repository.launch(device) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 6091ab0e..1aa79672 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -73,6 +73,7 @@ class CSCRepository @Inject constructor( val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(CSCService::class.java, device) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt index 5cbde590..59043ad6 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt @@ -39,7 +39,8 @@ internal data class HRSServiceData( val bodySensorLocation: Int? = null, val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, - val zoomIn: Boolean = false + val zoomIn: Boolean = false, + val deviceName: String? = null ) { val heartRates = data.map { it.heartRate } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 172de950..e2df0947 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -87,6 +87,7 @@ class HRSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(HRSService::class.java, device) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index 12ae54b4..2416e1ae 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -55,14 +55,13 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun HRSScreen() { val viewModel: HRSViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUpEvent) } Scaffold( topBar = { ProfileAppBar( - deviceName = deviceName, + deviceName = state.deviceName, connectionState = state.connectionState, title = R.string.hrs_title, navigateUp = navigateUp, diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index 9ddfcffa..8f836513 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -67,9 +65,6 @@ internal class HRSViewModel @Inject constructor( val state = repository.data - private val _deviceName = MutableStateFlow(null) - val deviceName = _deviceName.asStateFlow() - init { repository.setOnScreen(true) @@ -102,7 +97,6 @@ internal class HRSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _deviceName.value = device.name repository.launch(device) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt index 870f452e..e16453b7 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt @@ -39,5 +39,6 @@ internal data class HTSServiceData( val data: HTSData = HTSData(), val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, - val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS + val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS, + val deviceName: String? = null ) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 93925047..73b336f0 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -88,6 +88,7 @@ class HTSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(HTSService::class.java, device) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 1360a79c..1c259915 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -31,7 +31,6 @@ package no.nordicsemi.android.hts.view -import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -56,14 +55,13 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun HTSScreen() { val viewModel: HTSViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUp) } Scaffold( topBar = { ProfileAppBar( - deviceName = deviceName, + deviceName = state.deviceName, connectionState = state.connectionState, title = R.string.hts_title, navigateUp = navigateUp, diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt index 64d4abb1..c7d64d71 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/viewmodel/HTSViewModel.kt @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -67,9 +65,6 @@ internal class HTSViewModel @Inject constructor( val state = repository.data - private val _deviceName = MutableStateFlow(null) - val deviceName = _deviceName.asStateFlow() - init { repository.setOnScreen(true) @@ -102,7 +97,6 @@ internal class HTSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _deviceName.value = device.name repository.launch(device) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt index 16edefff..5d20f4b2 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -10,7 +10,8 @@ data class PRXServiceData( val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, val connectionStatus: BleGattConnectionStatus? = null, - val isRemoteAlarm: Boolean = false + val isRemoteAlarm: Boolean = false, + val deviceName: String? = null ) { val isLinkLossDisconnected = connectionStatus?.isLinkLoss ?: false diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index d0eab65b..748f8c57 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -89,6 +89,7 @@ class PRXRepository @Inject internal constructor( fun launch(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(PRXService::class.java, device) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index 68fc6579..aaa1faa7 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -55,14 +55,13 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun PRXScreen() { val viewModel: PRXViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUpEvent) } Scaffold( topBar = { ProfileAppBar( - deviceName = deviceName, + deviceName = state.deviceName, connectionState = state.connectionState, title = R.string.prx_title, navigateUp = navigateUp, diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt index 85fdf104..3d88ffb3 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/viewmodel/PRXViewModel.kt @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.distinctUntilChanged import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn @@ -73,9 +71,6 @@ internal class PRXViewModel @Inject constructor( val state = repository.data - private val _deviceName = MutableStateFlow(null) - val deviceName = _deviceName.asStateFlow() - init { repository.setOnScreen(true) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt index 4d422e49..5ac52c91 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt @@ -40,7 +40,8 @@ import no.nordicsemi.android.rscs.R internal data class RSCSServiceData( val data: RSCSData = RSCSData(), val batteryLevel: Int? = null, - val connectionState: GattConnectionStateWithStatus? = null + val connectionState: GattConnectionStateWithStatus? = null, + val deviceName: String? = null ) { @Composable fun displayActivity(): String { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 6f6fdbc2..ada36280 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -87,6 +87,7 @@ class RSCSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(RSCSService::class.java, device) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index e0c73433..4a5bb612 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -55,14 +55,13 @@ import no.nordicsemi.android.ui.view.ProfileAppBar fun RSCSScreen() { val viewModel: RSCSViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - val deviceName = viewModel.deviceName.collectAsState().value val navigateUp = { viewModel.onEvent(NavigateUpEvent) } Scaffold( topBar = { ProfileAppBar( - deviceName = deviceName, + deviceName = state.deviceName, connectionState = state.connectionState, title = R.string.rscs_title, navigateUp = navigateUp, diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 46b6e379..88cb6543 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -35,8 +35,6 @@ import android.os.ParcelUuid 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.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -66,10 +64,6 @@ internal class RSCSViewModel @Inject constructor( val state = repository.data - private val _deviceName = MutableStateFlow(null) - val deviceName = _deviceName.asStateFlow() - - init { viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { @@ -100,7 +94,6 @@ internal class RSCSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _deviceName.value = device.name repository.launch(device) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt index 33f120c3..3896801f 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt @@ -36,7 +36,8 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus internal data class UARTServiceData( val messages: List = emptyList(), val connectionState: GattConnectionStateWithStatus? = null, - val batteryLevel: Int? = null + val batteryLevel: Int? = null, + val deviceName: String? = null ) { val displayMessages = messages diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 979d9603..fda3ef5c 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -95,6 +95,7 @@ class UARTRepository @Inject internal constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(UARTService::class.java, device) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index 33fb4c2a..7f426b20 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -68,7 +68,7 @@ fun UARTScreen() { Scaffold( topBar = { ProfileAppBar( - deviceName = state.deviceName, + deviceName = state.uartManagerState.deviceName, connectionState = state.uartManagerState.connectionState, title = R.string.uart_title, navigateUp = navigateUp, diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt index 7e7f7631..b3f1cc70 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTState.kt @@ -41,8 +41,7 @@ internal data class UARTViewState( val isConfigurationEdited: Boolean = false, val configurations: List = emptyList(), val uartManagerState: UARTServiceData = UARTServiceData(), - val isInputVisible: Boolean = true, - val deviceName: String? = null + val isInputVisible: Boolean = true ) { val showEditDialog: Boolean = editedPosition != null diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index c29a1a74..17cd4cff 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -134,7 +134,6 @@ internal class UARTViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _state.value = _state.value.copy(deviceName = device.name) repository.launch(device) } From 5145266bdcf9ef702f5f25763c4762def1b46b91 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 28 Apr 2023 10:38:09 +0200 Subject: [PATCH 068/101] Apply fixes --- .../android/bps/viewmodel/BPSViewModel.kt | 2 ++ .../android/cgms/repository/CGMRepository.kt | 2 +- .../android/csc/repository/CSCRepository.kt | 22 ++++++++++++++++++- .../android/csc/repository/CSCService.kt | 8 +++++++ .../android/csc/viewmodel/CSCViewModel.kt | 7 ++++++ .../gls/main/viewmodel/GLSViewModel.kt | 6 +++-- .../android/hrs/service/HRSService.kt | 4 ++-- .../android/prx/repository/PRXService.kt | 9 +++++--- .../android/rscs/repository/RSCSService.kt | 4 ++-- .../android/uart/repository/UARTService.kt | 4 ++-- 10 files changed, 55 insertions(+), 13 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 33ec55a6..debe10bb 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -123,6 +123,8 @@ internal class BPSViewModel @Inject constructor( client = device.connect(context, logger = logger) + client.waitForBonding() + client.connectionStateWithStatus .filterNotNull() .onEach { onDataUpdate(it) } 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 44a3e917..451c16fb 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 @@ -127,7 +127,7 @@ class CGMRepository @Inject constructor( _stopEvent.tryEmit(DisconnectAndStopEvent()) } - fun clean() { + private fun clean() { _data.value = CGMServiceData() } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 1aa79672..152b0eb9 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -72,6 +72,23 @@ class CSCRepository @Inject constructor( val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } + private var isOnScreen = false + private var isServiceRunning = false + + fun setOnScreen(isOnScreen: Boolean) { + this.isOnScreen = isOnScreen + + if (shouldClean()) clean() + } + + fun setServiceRunning(serviceRunning: Boolean) { + this.isServiceRunning = serviceRunning + + if (shouldClean()) clean() + } + + private fun shouldClean() = !isOnScreen && !isServiceRunning + fun launch(device: ServerDevice) { _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(CSCService::class.java, device) @@ -102,7 +119,10 @@ class CSCRepository @Inject constructor( } fun disconnect() { - _data.value = CSCServiceData() _stopEvent.tryEmit(DisconnectAndStopEvent()) } + + private fun clean() { + _data.value = CSCServiceData() + } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index de72c23e..608e2c82 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -76,6 +76,8 @@ internal class CSCService : NotificationService() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) + repository.setServiceRunning(true) + val device = intent!!.getParcelableExtra(DEVICE_DATA)!! startGattClient(device) @@ -139,4 +141,10 @@ internal class CSCService : NotificationService() { private fun disconnect() { client.disconnect() } + + override fun onDestroy() { + super.onDestroy() + + repository.setServiceRunning(false) + } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index 743647f4..dc447ac7 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -68,6 +68,8 @@ internal class CSCViewModel @Inject constructor( val state = repository.data init { + repository.setOnScreen(true) + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -118,4 +120,9 @@ internal class CSCViewModel @Inject constructor( repository.disconnect() navigationManager.navigateUp() } + + override fun onCleared() { + super.onCleared() + repository.setOnScreen(false) + } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 543364d1..3719852b 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -157,6 +157,8 @@ internal class GLSViewModel @Inject constructor( } private fun startGattClient(device: ServerDevice) = viewModelScope.launch { + _state.value = _state.value.copy(deviceName = device.name) + logger = NordicBlekLogger(context, stringConst.APP_NAME, "GLS", device.address) client = device.connect(context, logger = logger) @@ -175,7 +177,7 @@ internal class GLSViewModel @Inject constructor( client.discoverServices() .filterNotNull() - .onEach { configureGatt(it, device) } + .onEach { configureGatt(it) } .launchIn(viewModelScope) } @@ -185,7 +187,7 @@ internal class GLSViewModel @Inject constructor( } } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + private suspend fun configureGatt(services: BleGattServices) { val glsService = services.findService(GLS_SERVICE_UUID)!! glucoseMeasurementCharacteristic = glsService.findCharacteristic(GM_CHARACTERISTIC)!! recordAccessControlPointCharacteristic = glsService.findCharacteristic(RACP_CHARACTERISTIC)!! diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 71cccdc0..dac43536 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -114,11 +114,11 @@ internal class HRSService : NotificationService() { client.discoverServices() .filterNotNull() - .onEach { configureGatt(it, device) } + .onEach { configureGatt(it) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + private suspend fun configureGatt(services: BleGattServices) { val hrsService = services.findService(HRS_SERVICE_UUID)!! val hrsMeasurementCharacteristic = hrsService.findCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID)!! val bodySensorLocationCharacteristic = hrsService.findCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID)!! diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 062a447f..58c57bac 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -46,6 +46,7 @@ import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectOptions import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty @@ -161,7 +162,9 @@ internal class PRXService : NotificationService() { private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { val logger = NordicBlekLogger(this@PRXService, stringConst.APP_NAME, "PRX", device.address) - client = device.connect(this@PRXService, logger = logger) + client = device.connect(this@PRXService, logger = logger, options = BleGattConnectOptions(autoConnect = true)) + + client.waitForBonding() repository.loggerEvent .onEach { logger.launch() } @@ -180,7 +183,7 @@ internal class PRXService : NotificationService() { client.discoverServices() .filterNotNull() - .onEach { configureGatt(it, device) } + .onEach { configureGatt(it) } .launchIn(lifecycleScope) repository.remoteAlarmLevel @@ -188,7 +191,7 @@ internal class PRXService : NotificationService() { .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + private suspend fun configureGatt(services: BleGattServices) { val prxService = services.findService(PRX_SERVICE_UUID)!! alertLevelCharacteristic = prxService.findCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)!! val linkLossService = services.findService(LINK_LOSS_SERVICE_UUID)!! diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index 17bae635..a8fd3054 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -110,11 +110,11 @@ internal class RSCSService : NotificationService() { client.discoverServices() .filterNotNull() - .onEach { configureGatt(it, device) } + .onEach { configureGatt(it) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { + private suspend fun configureGatt(services: BleGattServices) { val rscsService = services.findService(RSCS_SERVICE_UUID)!! val rscsMeasurementCharacteristic = rscsService.findCharacteristic(RSC_MEASUREMENT_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 32264bda..89ef3f86 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -116,11 +116,11 @@ internal class UARTService : NotificationService() { client.discoverServices() .filterNotNull() - .onEach { configureGatt(it, device, logger) } + .onEach { configureGatt(it, logger) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices, device: ServerDevice, logger: NordicBlekLogger) { + private suspend fun configureGatt(services: BleGattServices, logger: NordicBlekLogger) { val uartService = services.findService(UART_SERVICE_UUID)!! val rxCharacteristic = uartService.findCharacteristic(UART_RX_CHARACTERISTIC_UUID)!! val txCharacteristic = uartService.findCharacteristic(UART_TX_CHARACTERISTIC_UUID)!! From 6c58901c12c97bd339dbdaf871f3557f08e35ac7 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 2 May 2023 12:21:52 +0200 Subject: [PATCH 069/101] Add error support --- .../android/bps/viewmodel/BPSViewModel.kt | 11 ++--- .../gls/main/viewmodel/GLSViewModel.kt | 45 ++++++++++++++----- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index debe10bb..cd4f6b70 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -40,6 +40,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -128,7 +129,6 @@ internal class BPSViewModel @Inject constructor( client.connectionStateWithStatus .filterNotNull() .onEach { onDataUpdate(it) } - .onEach { stopIfDisconnected(it.state) } .onEach { logAnalytics(it.state) } .launchIn(viewModelScope) @@ -152,16 +152,19 @@ internal class BPSViewModel @Inject constructor( batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } .onEach { onDataUpdate(it) } + .catch { it.printStackTrace() } .launchIn(viewModelScope) bpmCharacteristic.getNotifications() .mapNotNull { BloodPressureMeasurementParser.parse(it) } .onEach { onDataUpdate(it) } + .catch { it.printStackTrace() } .launchIn(viewModelScope) icpCharacteristic?.getNotifications() ?.mapNotNull { IntermediateCuffPressureParser.parse(it) } ?.onEach { onDataUpdate(it) } + ?.catch { it.printStackTrace() } ?.launchIn(viewModelScope) } @@ -185,12 +188,6 @@ internal class BPSViewModel @Inject constructor( _state.value = _state.value.copy(result = newResult) } - private fun stopIfDisconnected(connectionState: GattConnectionState) { - if (connectionState == GattConnectionState.STATE_DISCONNECTED) { - navigationManager.navigateUp() - } - } - private fun logAnalytics(connectionState: GattConnectionState) { if (connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.BPS)) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 3719852b..50917bff 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -40,6 +40,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -61,6 +62,7 @@ import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected import no.nordicsemi.android.gls.main.view.OpenLoggerEvent import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.errors.GattOperationException import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -178,6 +180,7 @@ internal class GLSViewModel @Inject constructor( client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } + .catch { it.printStackTrace() } .launchIn(viewModelScope) } @@ -197,21 +200,25 @@ internal class GLSViewModel @Inject constructor( batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } .onEach { _state.value = _state.value.copyWithNewBatteryLevel(it) } + .catch { it.printStackTrace() } .launchIn(viewModelScope) glucoseMeasurementCharacteristic.getNotifications() .mapNotNull { GlucoseMeasurementParser.parse(it) } .onEach { _state.value = _state.value.copyWithNewRecord(it) } + .catch { it.printStackTrace() } .launchIn(viewModelScope) glsService.findCharacteristic(GM_CONTEXT_CHARACTERISTIC)?.getNotifications() ?.mapNotNull { GlucoseMeasurementContextParser.parse(it) } ?.onEach { _state.value = _state.value.copyWithNewContext(it) } + ?.catch { it.printStackTrace() } ?.launchIn(viewModelScope) recordAccessControlPointCharacteristic.getNotifications() .mapNotNull { RecordAccessControlPointParser.parse(it) } .onEach { onAccessControlPointDataReceived(it) } + .catch { it.printStackTrace() } .launchIn(viewModelScope) } @@ -246,14 +253,18 @@ internal class GLSViewModel @Inject constructor( private suspend fun onNumberOfRecordsReceived(numberOfRecords: Int) { if (numberOfRecords > 0) { - if (state.value.glsServiceData.records.isNotEmpty()) { - recordAccessControlPointCharacteristic.write( - RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(highestSequenceNumber).value - ) - } else { - recordAccessControlPointCharacteristic.write( - RecordAccessControlPointInputParser.reportAllStoredRecords().value - ) + try { + if (state.value.glsServiceData.records.isNotEmpty()) { + recordAccessControlPointCharacteristic.write( + RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(highestSequenceNumber).value + ) + } else { + recordAccessControlPointCharacteristic.write( + RecordAccessControlPointInputParser.reportAllStoredRecords().value + ) + } + } catch (e: GattOperationException) { + e.printStackTrace() } } _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.SUCCESS) @@ -274,18 +285,30 @@ internal class GLSViewModel @Inject constructor( private suspend fun requestLastRecord() { clear() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) + try { + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) + } catch (e: Exception) { + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) + } } private suspend fun requestFirstRecord() { clear() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) + try { + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) + } catch (e: Exception) { + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) + } } private suspend fun requestAllRecords() { clear() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) + try { + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) + } catch (e: Exception) { + e.printStackTrace() + } } } From f8034ac4627ec9cec8ca1d6c78057041fec70663 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 3 May 2023 10:41:10 +0200 Subject: [PATCH 070/101] Add Java 17 to Github Actions --- .github/workflows/deploy-all.yml | 8 ++++++++ .github/workflows/deploy-to-play-store.yml | 4 ++++ .github/workflows/generate-readme.yml | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/.github/workflows/deploy-all.yml b/.github/workflows/deploy-all.yml index a07bfec4..ac5f3e56 100644 --- a/.github/workflows/deploy-all.yml +++ b/.github/workflows/deploy-all.yml @@ -11,6 +11,10 @@ jobs: with: ref: main fetch-depth: 0 + - uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: '17' - shell: bash run: | git config user.email mag@nordicsemi.no @@ -29,6 +33,10 @@ jobs: with: ref: main fetch-depth: 0 + - uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: '17' - shell: bash env: KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} diff --git a/.github/workflows/deploy-to-play-store.yml b/.github/workflows/deploy-to-play-store.yml index 37208f65..b0856553 100644 --- a/.github/workflows/deploy-to-play-store.yml +++ b/.github/workflows/deploy-to-play-store.yml @@ -8,6 +8,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 + - uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: '17' - shell: bash env: KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }} diff --git a/.github/workflows/generate-readme.yml b/.github/workflows/generate-readme.yml index feba80f7..92b7c41c 100644 --- a/.github/workflows/generate-readme.yml +++ b/.github/workflows/generate-readme.yml @@ -8,6 +8,10 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 + - uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: '17' - shell: bash env: CONTACT_EMAIL: ${{ secrets.CONTACT_EMAIL }} From 191b3a0ce50cf64a58141f8ea2a1a63332c111f0 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 3 May 2023 10:43:05 +0200 Subject: [PATCH 071/101] Bump gradle --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a2bd231e..d749aba9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -31,7 +31,7 @@ #Mon Feb 14 14:46:55 CET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From d6f6abfbb4d777119b6c55f288e6584247683347 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 3 May 2023 11:59:52 +0200 Subject: [PATCH 072/101] Bump version --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 0ba54cf1..c39e9e55 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.4.3") + from("no.nordicsemi.android.gradle:version-catalog:1.4.6") } } } From 1c45bf09d1cf8b513b93c0b56a453299add14ff2 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 3 May 2023 15:31:17 +0200 Subject: [PATCH 073/101] Add catch to discoverServices --- .../java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt | 1 + .../java/no/nordicsemi/android/cgms/repository/CGMService.kt | 2 ++ .../java/no/nordicsemi/android/csc/repository/CSCService.kt | 2 ++ .../main/java/no/nordicsemi/android/hrs/service/HRSService.kt | 2 ++ .../java/no/nordicsemi/android/hts/repository/HTSService.kt | 2 ++ .../java/no/nordicsemi/android/prx/repository/PRXService.kt | 2 ++ .../java/no/nordicsemi/android/rscs/repository/RSCSService.kt | 2 ++ .../java/no/nordicsemi/android/uart/repository/UARTService.kt | 2 ++ 8 files changed, 15 insertions(+) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index cd4f6b70..d6c38aa9 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -139,6 +139,7 @@ internal class BPSViewModel @Inject constructor( client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } + .catch { it.printStackTrace() } .launchIn(viewModelScope) } 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 03cb77c4..901d6513 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 @@ -35,6 +35,7 @@ import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -155,6 +156,7 @@ internal class CGMService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 608e2c82..7717a4c5 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -35,6 +35,7 @@ import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -111,6 +112,7 @@ internal class CSCService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index dac43536..8a4dc101 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -35,6 +35,7 @@ import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -115,6 +116,7 @@ internal class HRSService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index a4c3f8bb..266e51da 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -35,6 +35,7 @@ import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -113,6 +114,7 @@ internal class HTSService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 58c57bac..7c47810f 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -35,6 +35,7 @@ import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -184,6 +185,7 @@ internal class PRXService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) repository.remoteAlarmLevel diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index a8fd3054..0cb8c76a 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -35,6 +35,7 @@ import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -111,6 +112,7 @@ internal class RSCSService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 89ef3f86..9efd9614 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -35,6 +35,7 @@ import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull @@ -117,6 +118,7 @@ internal class UARTService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it, logger) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } From 736c753c5e6f9b7076f0d39cd717ccd3301eec0e Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 3 May 2023 16:02:44 +0200 Subject: [PATCH 074/101] Fix waitForBonding position on HRS profile --- .../main/java/no/nordicsemi/android/hrs/service/HRSService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 8a4dc101..632ad906 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -97,6 +97,8 @@ internal class HRSService : NotificationService() { client = device.connect(this@HRSService, logger = logger) + client.waitForBonding() + repository.loggerEvent .onEach { logger.launch() } .launchIn(lifecycleScope) @@ -107,8 +109,6 @@ internal class HRSService : NotificationService() { .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) - client.waitForBonding() - if (!client.isConnected) { return@launch } From 8283d210cc72572b4d851f4317d1c9fcd6c33065 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Wed, 3 May 2023 16:11:25 +0200 Subject: [PATCH 075/101] Catch exceptions --- .../android/cgms/repository/CGMService.kt | 26 ++++++++++++++++--- .../android/csc/repository/CSCService.kt | 2 ++ .../android/hrs/service/HRSService.kt | 2 ++ .../android/hts/repository/HTSService.kt | 2 ++ .../android/prx/repository/PRXService.kt | 11 +++++--- .../android/rscs/repository/RSCSService.kt | 2 ++ .../android/uart/repository/UARTService.kt | 2 ++ 7 files changed, 41 insertions(+), 6 deletions(-) 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 901d6513..781a729e 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 @@ -47,6 +47,7 @@ import no.nordicsemi.android.cgms.data.CGMServiceCommand import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.errors.GattOperationException import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -173,6 +174,7 @@ internal class CGMService : NotificationService() { batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } .onEach { repository.onBatteryLevelChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) measurementCharacteristic.getNotifications() @@ -190,6 +192,7 @@ internal class CGMService : NotificationService() { repository.onDataReceived(result) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) opsControlPointCharacteristic.getNotifications() @@ -209,11 +212,13 @@ internal class CGMService : NotificationService() { } } } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) recordAccessControlPointCharacteristic.getNotifications() .mapNotNull { RecordAccessControlPointParser.parse(it) } .onEach { onAccessControlPointDataReceived(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) lifecycleScope.launchWithCatch { @@ -292,19 +297,34 @@ internal class CGMService : NotificationService() { private suspend fun requestLastRecord() { clear() repository.onNewRequestStatus(RequestStatus.PENDING) - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) + try { + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) + } catch (e: GattOperationException) { + e.printStackTrace() + repository.onNewRequestStatus(RequestStatus.FAILED) + } } private suspend fun requestFirstRecord() { clear() repository.onNewRequestStatus(RequestStatus.PENDING) - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) + try { + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) + } catch (e: GattOperationException) { + e.printStackTrace() + repository.onNewRequestStatus(RequestStatus.FAILED) + } } private suspend fun requestAllRecords() { clear() repository.onNewRequestStatus(RequestStatus.PENDING) - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) + try { + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) + } catch (e: GattOperationException) { + e.printStackTrace() + repository.onNewRequestStatus(RequestStatus.FAILED) + } } private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 7717a4c5..f50726ad 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -125,12 +125,14 @@ internal class CSCService : NotificationService() { batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } .onEach { repository.onBatteryLevelChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) val cscDataParser = CSCDataParser() cscMeasurementCharacteristic.getNotifications() .mapNotNull { cscDataParser.parse(it, repository.wheelSize.value) } .onEach { repository.onCSCDataChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 632ad906..acdecd09 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -133,11 +133,13 @@ internal class HRSService : NotificationService() { batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } .onEach { repository.onBatteryLevelChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) hrsMeasurementCharacteristic.getNotifications() .mapNotNull { HRSDataParser.parse(it) } .onEach { repository.onHRSDataChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 266e51da..0abb94f8 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -127,11 +127,13 @@ internal class HTSService : NotificationService() { batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } .onEach { repository.onBatteryLevelChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) htsMeasurementCharacteristic.getNotifications() .mapNotNull { HTSDataParser.parse(it) } .onEach { repository.onHTSDataChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 7c47810f..4753d277 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -44,6 +44,7 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.errors.GattOperationException import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -174,7 +175,6 @@ internal class PRXService : NotificationService() { client.connectionStateWithStatus .filterNotNull() .onEach { repository.onConnectionStateChanged(it) } - .filterNotNull() .onEach { stopIfDisconnected(it.state, it.status) } .launchIn(lifecycleScope) @@ -204,14 +204,19 @@ internal class PRXService : NotificationService() { batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } .onEach { repository.onBatteryLevelChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) linkLossCharacteristic.write(AlertLevelInputParser.parse(AlarmLevel.HIGH)) } private suspend fun writeAlertLevel(alarmLevel: AlarmLevel) { - alertLevelCharacteristic.write(AlertLevelInputParser.parse(alarmLevel), BleWriteType.NO_RESPONSE) - repository.onRemoteAlarmLevelSet(alarmLevel) + try { + alertLevelCharacteristic.write(AlertLevelInputParser.parse(alarmLevel), BleWriteType.NO_RESPONSE) + repository.onRemoteAlarmLevelSet(alarmLevel) + } catch (e: GattOperationException) { + e.printStackTrace() + } } private fun stopIfDisconnected(connectionState: GattConnectionState, connectionStatus: BleGattConnectionStatus) { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index 0cb8c76a..c65e1058 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -125,11 +125,13 @@ internal class RSCSService : NotificationService() { batteryLevelCharacteristic.getNotifications() .mapNotNull { BatteryLevelParser.parse(it) } .onEach { repository.onBatteryLevelChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) rscsMeasurementCharacteristic.getNotifications() .mapNotNull { RSCSDataParser.parse(it) } .onEach { repository.onRSCSDataChanged(it) } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 9efd9614..0a0046ba 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -132,11 +132,13 @@ internal class UARTService : NotificationService() { batteryService?.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)?.getNotifications() ?.mapNotNull { BatteryLevelParser.parse(it) } ?.onEach { repository.onBatteryLevelChanged(it) } + ?.catch { it.printStackTrace() } ?.launchIn(lifecycleScope) txCharacteristic.getNotifications() .onEach { repository.onNewMessageReceived(String(it)) } .onEach { logger.log(10, "Received: ${String(it)}") } + .catch { it.printStackTrace() } .launchIn(lifecycleScope) repository.command From fb5c2730af5616310ccbf6f3c409fb1863dc1589 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 4 May 2023 15:12:21 +0200 Subject: [PATCH 076/101] Add missing services message --- .../nordicsemi/android/bps/view/BPSScreen.kt | 2 +- .../android/bps/view/BPSViewState.kt | 13 ++++++++++-- .../android/bps/viewmodel/BPSViewModel.kt | 14 +++++++++++-- .../android/cgms/data/CGMServiceData.kt | 13 ++++++++++-- .../android/cgms/repository/CGMRepository.kt | 5 +++++ .../android/cgms/repository/CGMService.kt | 2 +- .../nordicsemi/android/cgms/view/CGMScreen.kt | 2 +- .../android/csc/data/CSCServiceData.kt | 13 ++++++++++-- .../android/csc/repository/CSCRepository.kt | 5 +++++ .../android/csc/repository/CSCService.kt | 2 +- .../nordicsemi/android/csc/view/CSCScreen.kt | 2 +- .../android/gls/main/view/GLSScreen.kt | 2 +- .../android/gls/main/view/GLSViewState.kt | 10 +++++++++- .../gls/main/viewmodel/GLSViewModel.kt | 14 +++++++++++-- .../android/hrs/data/HRSServiceData.kt | 11 +++++++++- .../android/hrs/service/HRSRepository.kt | 5 +++++ .../android/hrs/service/HRSService.kt | 2 +- .../nordicsemi/android/hrs/view/HRSScreen.kt | 2 +- .../android/hts/data/HTSServiceData.kt | 13 ++++++++++-- .../android/hts/repository/HTSRepository.kt | 20 +++++++++++++------ .../android/hts/repository/HTSService.kt | 15 ++------------ .../nordicsemi/android/hts/view/HTSScreen.kt | 2 +- .../android/prx/data/PRXServiceData.kt | 9 ++++++++- .../android/prx/repository/PRXRepository.kt | 5 +++++ .../android/prx/repository/PRXService.kt | 12 ++++++----- .../nordicsemi/android/prx/view/PRXScreen.kt | 2 +- .../android/rscs/data/RSCSServiceData.kt | 11 +++++++++- .../android/rscs/repository/RSCSRepository.kt | 5 +++++ .../android/rscs/repository/RSCSService.kt | 2 +- .../android/rscs/view/RSCSScreen.kt | 2 +- .../android/rscs/viewmodel/RSCSViewModel.kt | 7 +++++++ .../android/uart/data/UARTServiceData.kt | 10 +++++++++- .../android/uart/repository/UARTRepository.kt | 5 +++++ .../android/uart/repository/UARTService.kt | 11 +--------- .../android/uart/view/UARTScreen.kt | 2 +- 35 files changed, 188 insertions(+), 64 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 2abb728c..da253bdb 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -80,7 +80,7 @@ fun BPSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.result.connectionState.status) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.disconnectStatus) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> BPSContentView(state.result) { viewModel.onEvent(it) } diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSViewState.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSViewState.kt index 23c10d02..4b9d617b 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSViewState.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSViewState.kt @@ -32,8 +32,17 @@ package no.nordicsemi.android.bps.view import no.nordicsemi.android.bps.data.BPSServiceData +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus internal data class BPSViewState( val result: BPSServiceData = BPSServiceData(), - val deviceName: String? = null -) + val deviceName: String? = null, + val missingServices: Boolean = false +) { + + val disconnectStatus = if (missingServices) { + BleGattConnectionStatus.NOT_SUPPORTED + } else { + result.connectionState?.status ?: BleGattConnectionStatus.UNKNOWN + } +} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index d6c38aa9..ccd99d4d 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -112,11 +112,16 @@ internal class BPSViewModel @Inject constructor( fun onEvent(event: BPSViewEvent) { when (event) { - DisconnectEvent -> navigationManager.navigateUp() + DisconnectEvent -> onDisconnectEvent() OpenLoggerEvent -> logger.launch() } } + private fun onDisconnectEvent() { + client.disconnect() + navigationManager.navigateUp() + } + private fun startGattClient(device: ServerDevice) = viewModelScope.launch { _state.value = _state.value.copy(deviceName = device.name) @@ -139,7 +144,7 @@ internal class BPSViewModel @Inject constructor( client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } - .catch { it.printStackTrace() } + .catch { onMissingServices() } .launchIn(viewModelScope) } @@ -169,6 +174,11 @@ internal class BPSViewModel @Inject constructor( ?.launchIn(viewModelScope) } + private fun onMissingServices() { + _state.value = _state.value.copy(missingServices = true) + client.disconnect() + } + private fun onDataUpdate(connectionState: GattConnectionStateWithStatus) { val newResult = _state.value.result.copy(connectionState = connectionState) _state.value = _state.value.copy(result = newResult) 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 2307c447..7c7133f1 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 @@ -1,5 +1,6 @@ package no.nordicsemi.android.cgms.data +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMRecord import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus @@ -9,8 +10,16 @@ internal data class CGMServiceData( val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, val requestStatus: RequestStatus = RequestStatus.IDLE, - val deviceName: String? = null -) + val deviceName: String? = null, + val missingServices: Boolean = false +) { + + val disconnectStatus = if (missingServices) { + BleGattConnectionStatus.NOT_SUPPORTED + } else { + connectionState?.status ?: BleGattConnectionStatus.UNKNOWN + } +} data class CGMRecordWithSequenceNumber( val sequenceNumber: Int, 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 451c16fb..1320fce0 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 @@ -115,6 +115,11 @@ class CGMRepository @Inject constructor( _data.value = _data.value.copy(requestStatus = requestStatus) } + fun onMissingServices() { + _data.value = _data.value.copy(missingServices = true) + _stopEvent.tryEmit(DisconnectAndStopEvent()) + } + fun openLogger() { _loggerEvent.tryEmit(OpenLoggerEvent()) } 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 781a729e..d52eedb5 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 @@ -157,7 +157,7 @@ internal class CGMService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } - .catch { it.printStackTrace() } + .catch { repository.onMissingServices() } .launchIn(lifecycleScope) } 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 121759ae..8cbd1b8d 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 @@ -81,7 +81,7 @@ fun CGMScreen() { GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.disconnectStatus) { NavigateUpButton(navigateUp) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt index 917c90df..1bb81d26 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceData.kt @@ -1,5 +1,6 @@ package no.nordicsemi.android.csc.data +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData @@ -8,5 +9,13 @@ internal data class CSCServiceData( val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, val speedUnit: SpeedUnit = SpeedUnit.M_S, - val deviceName: String? = null -) + val deviceName: String? = null, + val missingServices: Boolean = false +) { + + val disconnectStatus = if (missingServices) { + BleGattConnectionStatus.NOT_SUPPORTED + } else { + connectionState?.status ?: BleGattConnectionStatus.UNKNOWN + } +} diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 152b0eb9..f74f5e05 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -114,6 +114,11 @@ class CSCRepository @Inject constructor( _data.value = _data.value.copy(data = cscData) } + fun onMissingServices() { + _data.value = _data.value.copy(missingServices = true) + _stopEvent.tryEmit(DisconnectAndStopEvent()) + } + fun openLogger() { _loggerEvent.tryEmit(OpenLoggerEvent()) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index f50726ad..1a5878d6 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -112,7 +112,7 @@ internal class CSCService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } - .catch { it.printStackTrace() } + .catch { repository.onMissingServices() } .launchIn(lifecycleScope) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 578b168b..7b6a9a9a 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -80,7 +80,7 @@ fun CSCScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.disconnectStatus) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> CSCContentView(state) { viewModel.onEvent(it) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index b8ecd090..cb5d84e9 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -80,7 +80,7 @@ fun GLSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.glsServiceData.connectionState.status) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.disconnectStatus) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> GLSContentView(state.glsServiceData) { viewModel.onEvent(it) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt index fd157f73..9d5125c3 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSViewState.kt @@ -32,6 +32,7 @@ package no.nordicsemi.android.gls.main.view import no.nordicsemi.android.gls.data.GLSServiceData +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSMeasurementContext import no.nordicsemi.android.kotlin.ble.profile.gls.data.GLSRecord @@ -39,9 +40,16 @@ import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus internal data class GLSViewState( val glsServiceData: GLSServiceData = GLSServiceData(), - val deviceName: String? = null + val deviceName: String? = null, + val missingServices: Boolean = false ) { + val disconnectStatus = if (missingServices) { + BleGattConnectionStatus.NOT_SUPPORTED + } else { + glsServiceData.connectionState?.status ?: BleGattConnectionStatus.UNKNOWN + } + fun copyWithNewConnectionState(connectionState: GattConnectionStateWithStatus): GLSViewState { return copy(glsServiceData = glsServiceData.copy(connectionState = connectionState)) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 50917bff..8a34c0bd 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -137,10 +137,15 @@ internal class GLSViewModel @Inject constructor( OpenLoggerEvent -> logger.launch() is OnWorkingModeSelected -> onEvent(event) is OnGLSRecordClick -> navigateToDetails(event.record) - DisconnectEvent -> navigationManager.navigateUp() + DisconnectEvent -> onDisconnectEvent() } } + private fun onDisconnectEvent() { + client.disconnect() + navigationManager.navigateUp() + } + private fun navigateToDetails(record: GLSRecord) { val context = state.value.glsServiceData.records[record] navigationManager.navigateTo(GlsDetailsDestinationId, record to context) @@ -180,10 +185,15 @@ internal class GLSViewModel @Inject constructor( client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } - .catch { it.printStackTrace() } + .catch { onMissingServices() } .launchIn(viewModelScope) } + private fun onMissingServices() { + _state.value = state.value.copy(missingServices = true) + client.disconnect() + } + private fun logAnalytics(connectionState: GattConnectionStateWithStatus) { if (connectionState.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.GLS)) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt index 59043ad6..b86fce95 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.hrs.data +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData @@ -40,7 +41,15 @@ internal data class HRSServiceData( val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, val zoomIn: Boolean = false, - val deviceName: String? = null + val deviceName: String? = null, + val missingServices: Boolean = false ) { + + val disconnectStatus = if (missingServices) { + BleGattConnectionStatus.NOT_SUPPORTED + } else { + connectionState?.status ?: BleGattConnectionStatus.UNKNOWN + } + val heartRates = data.map { it.heartRate } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index e2df0947..c6f9b810 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -111,6 +111,11 @@ class HRSRepository @Inject constructor( _data.value = _data.value.copy(batteryLevel = batteryLevel) } + fun onMissingServices() { + _data.value = _data.value.copy(missingServices = true) + _stopEvent.tryEmit(DisconnectAndStopEvent()) + } + fun openLogger() { _loggerEvent.tryEmit(OpenLoggerEvent()) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index acdecd09..4d1f66a8 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -116,7 +116,7 @@ internal class HRSService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } - .catch { it.printStackTrace() } + .catch { repository.onMissingServices() } .launchIn(lifecycleScope) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index 2416e1ae..70b67d4a 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -80,7 +80,7 @@ fun HRSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.disconnectStatus) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> HRSContentView(state) { viewModel.onEvent(it) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt index e16453b7..d56e3f85 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/data/HTSServiceData.kt @@ -32,6 +32,7 @@ package no.nordicsemi.android.hts.data import no.nordicsemi.android.hts.view.TemperatureUnit +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData @@ -40,5 +41,13 @@ internal data class HTSServiceData( val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, val temperatureUnit: TemperatureUnit = TemperatureUnit.CELSIUS, - val deviceName: String? = null -) + val deviceName: String? = null, + val missingServices: Boolean = false +) { + + val disconnectStatus = if (missingServices) { + BleGattConnectionStatus.NOT_SUPPORTED + } else { + connectionState?.status ?: BleGattConnectionStatus.UNKNOWN + } +} diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 73b336f0..e1714b94 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -46,8 +46,8 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData import no.nordicsemi.android.service.DisconnectAndStopEvent -import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager +import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -55,7 +55,8 @@ import javax.inject.Singleton class HTSRepository @Inject constructor( @ApplicationContext private val context: Context, - private val serviceManager: ServiceManager + private val serviceManager: ServiceManager, + private val stringConst: StringConst ) { private var logger: NordicBlekLogger? = null @@ -65,9 +66,6 @@ class HTSRepository @Inject constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() - private val _loggerEvent = simpleSharedFlow() - internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } private var isOnScreen = false @@ -89,6 +87,7 @@ class HTSRepository @Inject constructor( fun launch(device: ServerDevice) { _data.value = _data.value.copy(deviceName = device.name) + logger = NordicBlekLogger(context, stringConst.APP_NAME, "HTS", device.address) serviceManager.startService(HTSService::class.java, device) } @@ -109,13 +108,22 @@ class HTSRepository @Inject constructor( } fun openLogger() { - _loggerEvent.tryEmit(OpenLoggerEvent()) + logger?.launch() + } + + fun log(priority: Int, message: String) { + logger?.log(priority, message) } fun disconnect() { _stopEvent.tryEmit(DisconnectAndStopEvent()) } + fun onMissingServices() { + _data.value = _data.value.copy(missingServices = true) + _stopEvent.tryEmit(DisconnectAndStopEvent()) + } + private fun clean() { logger = null _data.value = HTSServiceData() diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 0abb94f8..9a0e9cf5 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -52,7 +51,6 @@ import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.hts.HTSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -69,9 +67,6 @@ internal class HTSService : NotificationService() { @Inject lateinit var repository: HTSRepository - @Inject - lateinit var stringConst: StringConst - private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -91,16 +86,10 @@ internal class HTSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicBlekLogger(this@HTSService, stringConst.APP_NAME, "HTS", device.address) - - client = device.connect(this@HTSService, logger = logger) + client = device.connect(this@HTSService, logger = { p, s -> repository.log(p, s) }) client.waitForBonding() - repository.loggerEvent - .onEach { logger.launch() } - .launchIn(lifecycleScope) - client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() @@ -114,7 +103,7 @@ internal class HTSService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } - .catch { it.printStackTrace() } + .catch { repository.onMissingServices() } .launchIn(lifecycleScope) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index 1c259915..f1b7bcdf 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -80,7 +80,7 @@ fun HTSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.disconnectStatus) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> HTSContentView(state) { viewModel.onEvent(it) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt index 5d20f4b2..7787532f 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -11,8 +11,15 @@ data class PRXServiceData( val connectionState: GattConnectionStateWithStatus? = null, val connectionStatus: BleGattConnectionStatus? = null, val isRemoteAlarm: Boolean = false, - val deviceName: String? = null + val deviceName: String? = null, + val missingServices: Boolean = false ) { + val disconnectStatus = if (missingServices) { + BleGattConnectionStatus.NOT_SUPPORTED + } else { + connectionState?.status ?: BleGattConnectionStatus.UNKNOWN + } + val isLinkLossDisconnected = connectionStatus?.isLinkLoss ?: false } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 748f8c57..01421843 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -121,6 +121,11 @@ class PRXRepository @Inject internal constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } + fun onMissingServices() { + _data.value = _data.value.copy(missingServices = true) + _stopEvent.tryEmit(DisconnectAndStopEvent()) + } + fun disconnect() { _remoteAlarmLevel.tryEmit(AlarmLevel.NONE) _stopEvent.tryEmit(DisconnectAndStopEvent()) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 4753d277..e1226158 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -90,7 +90,7 @@ internal class PRXService : NotificationService() { private lateinit var client: BleGattClient private lateinit var server: BleGattServer - private lateinit var alertLevelCharacteristic: BleGattCharacteristic + private var alertLevelCharacteristic: BleGattCharacteristic? = null override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -185,7 +185,7 @@ internal class PRXService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } - .catch { it.printStackTrace() } + .catch { repository.onMissingServices() } .launchIn(lifecycleScope) repository.remoteAlarmLevel @@ -212,9 +212,11 @@ internal class PRXService : NotificationService() { private suspend fun writeAlertLevel(alarmLevel: AlarmLevel) { try { - alertLevelCharacteristic.write(AlertLevelInputParser.parse(alarmLevel), BleWriteType.NO_RESPONSE) - repository.onRemoteAlarmLevelSet(alarmLevel) - } catch (e: GattOperationException) { + alertLevelCharacteristic?.run { + write(AlertLevelInputParser.parse(alarmLevel), BleWriteType.NO_RESPONSE) + repository.onRemoteAlarmLevelSet(alarmLevel) + } + } catch (e: Exception) { e.printStackTrace() } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index aaa1faa7..7fb84f6d 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -80,7 +80,7 @@ fun PRXScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.disconnectStatus) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> ContentView(state) { viewModel.onEvent(it) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt index 5ac52c91..6251a857 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt @@ -33,6 +33,7 @@ package no.nordicsemi.android.rscs.data import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.rscs.data.RSCSData import no.nordicsemi.android.rscs.R @@ -41,8 +42,16 @@ internal data class RSCSServiceData( val data: RSCSData = RSCSData(), val batteryLevel: Int? = null, val connectionState: GattConnectionStateWithStatus? = null, - val deviceName: String? = null + val deviceName: String? = null, + val missingServices: Boolean = false ) { + + val disconnectStatus = if (missingServices) { + BleGattConnectionStatus.NOT_SUPPORTED + } else { + connectionState?.status ?: BleGattConnectionStatus.UNKNOWN + } + @Composable fun displayActivity(): String { return if (data.running) { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index ada36280..672c88ed 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -103,6 +103,11 @@ class RSCSRepository @Inject constructor( _data.value = _data.value.copy(batteryLevel = batteryLevel) } + fun onMissingServices() { + _data.value = _data.value.copy(missingServices = true) + _stopEvent.tryEmit(DisconnectAndStopEvent()) + } + fun openLogger() { _loggerEvent.tryEmit(OpenLoggerEvent()) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index c65e1058..fca9ef0f 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -112,7 +112,7 @@ internal class RSCSService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it) } - .catch { it.printStackTrace() } + .catch { repository.onMissingServices() } .launchIn(lifecycleScope) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index 4a5bb612..a99e4800 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -80,7 +80,7 @@ fun RSCSScreen() { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, - GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.connectionState.status) { + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(state.disconnectStatus) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> RSCSContentView(state) { viewModel.onEvent(it) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 88cb6543..74309784 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -65,6 +65,8 @@ internal class RSCSViewModel @Inject constructor( val state = repository.data init { + repository.setOnScreen(true) + viewModelScope.launch { if (repository.isRunning.firstOrNull() == false) { requestBluetoothDevice() @@ -109,4 +111,9 @@ internal class RSCSViewModel @Inject constructor( repository.disconnect() navigationManager.navigateUp() } + + override fun onCleared() { + super.onCleared() + repository.setOnScreen(false) + } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt index 3896801f..ee7b0ac8 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTServiceData.kt @@ -31,15 +31,23 @@ package no.nordicsemi.android.uart.data +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus internal data class UARTServiceData( val messages: List = emptyList(), val connectionState: GattConnectionStateWithStatus? = null, val batteryLevel: Int? = null, - val deviceName: String? = null + val deviceName: String? = null, + val missingServices: Boolean = false ) { + val disconnectStatus = if (missingServices) { + BleGattConnectionStatus.NOT_SUPPORTED + } else { + connectionState?.status ?: BleGattConnectionStatus.UNKNOWN + } + val displayMessages = messages } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index fda3ef5c..5e03d40e 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -134,6 +134,11 @@ class UARTRepository @Inject internal constructor( _loggerEvent.tryEmit(OpenLoggerEvent()) } + fun onMissingServices() { + _data.value = _data.value.copy(missingServices = true) + _stopEvent.tryEmit(DisconnectAndStopEvent()) + } + suspend fun saveConfigurationName(name: String) { configurationDataSource.saveConfigurationName(name) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 0a0046ba..432e0554 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -49,8 +49,6 @@ import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty import no.nordicsemi.android.kotlin.ble.core.data.BleWriteType -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState -import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.core.data.Mtu import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.service.DEVICE_DATA @@ -108,7 +106,6 @@ internal class UARTService : NotificationService() { client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() - .onEach { stopIfDisconnected(it) } .launchIn(lifecycleScope) if (!client.isConnected) { @@ -118,7 +115,7 @@ internal class UARTService : NotificationService() { client.discoverServices() .filterNotNull() .onEach { configureGatt(it, logger) } - .catch { it.printStackTrace() } + .catch { repository.onMissingServices() } .launchIn(lifecycleScope) } @@ -156,12 +153,6 @@ internal class UARTService : NotificationService() { } } - private fun stopIfDisconnected(connectionState: GattConnectionStateWithStatus) { - if (connectionState.state == GattConnectionState.STATE_DISCONNECTED) { - stopSelf() - } - } - private fun disconnect() { client.disconnect() } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index 7f426b20..71355fc3 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -85,7 +85,7 @@ fun UARTScreen() { GattConnectionState.STATE_CONNECTING -> PaddingBox { DeviceConnectingView { NavigateUpButton(navigateUp) } } GattConnectionState.STATE_DISCONNECTED, GattConnectionState.STATE_DISCONNECTING -> PaddingBox { - DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + DeviceDisconnectedView(state.uartManagerState.disconnectStatus) { NavigateUpButton(navigateUp) } } GattConnectionState.STATE_CONNECTED -> SuccessScreen() } From 018eb13a1fd20ef4c5cde3f077f673128bf30257 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 4 May 2023 16:16:06 +0200 Subject: [PATCH 077/101] Make logger button working after disconnect --- .../android/cgms/repository/CGMRepository.kt | 17 ++++++++++----- .../android/cgms/repository/CGMService.kt | 11 +--------- .../android/csc/repository/CSCRepository.kt | 16 ++++++++++---- .../android/csc/repository/CSCService.kt | 11 +--------- .../android/hrs/service/HRSRepository.kt | 15 ++++++++----- .../android/hrs/service/HRSService.kt | 13 +----------- .../android/prx/repository/PRXRepository.kt | 19 ++++++++++------- .../android/prx/repository/PRXService.kt | 15 +++++-------- .../android/rscs/repository/RSCSRepository.kt | 15 +++++++------ .../android/rscs/repository/RSCSService.kt | 11 +--------- .../android/uart/repository/UARTRepository.kt | 18 +++++++++++----- .../android/uart/repository/UARTService.kt | 21 +++++-------------- 12 files changed, 82 insertions(+), 100 deletions(-) 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 1320fce0..ec7f73fc 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 @@ -41,13 +41,14 @@ import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber import no.nordicsemi.android.cgms.data.CGMServiceCommand import no.nordicsemi.android.cgms.data.CGMServiceData import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus import no.nordicsemi.android.service.DisconnectAndStopEvent -import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager +import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -56,7 +57,10 @@ class CGMRepository @Inject constructor( @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, + private val stringConst: StringConst ) { + private var logger: NordicBlekLogger? = null + private val _data = MutableStateFlow(CGMServiceData()) internal val data = _data.asStateFlow() @@ -66,9 +70,6 @@ class CGMRepository @Inject constructor( private val _command = simpleSharedFlow() internal val command = _command.asSharedFlow() - private val _loggerEvent = simpleSharedFlow() - internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } val hasRecords = data.value.records.isNotEmpty() val highestSequenceNumber = data.value.records.maxOfOrNull { it.sequenceNumber } ?: -1 @@ -91,6 +92,7 @@ class CGMRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + logger = NordicBlekLogger(context, stringConst.APP_NAME, "CGM", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(CGMService::class.java, device) } @@ -121,7 +123,11 @@ class CGMRepository @Inject constructor( } fun openLogger() { - _loggerEvent.tryEmit(OpenLoggerEvent()) + logger?.launch() + } + + fun log(priority: Int, message: String) { + logger?.log(priority, message) } fun clear() { @@ -133,6 +139,7 @@ class CGMRepository @Inject constructor( } private fun clean() { + logger = null _data.value = CGMServiceData() } } 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 d52eedb5..b6e6975c 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 @@ -93,9 +93,6 @@ internal class CGMService : NotificationService() { @Inject lateinit var repository: CGMRepository - @Inject - lateinit var stringConst: StringConst - private lateinit var client: BleGattClient private var secured = false @@ -136,13 +133,7 @@ internal class CGMService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicBlekLogger(this@CGMService, stringConst.APP_NAME, "CGM", device.address) - - client = device.connect(this@CGMService, logger = logger) - - repository.loggerEvent - .onEach { logger.launch() } - .launchIn(lifecycleScope) + client = device.connect(this@CGMService, logger = { p, s -> repository.log(p, s) }) client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index f74f5e05..61750958 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -49,6 +50,7 @@ import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSizes import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager +import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -57,7 +59,10 @@ class CSCRepository @Inject constructor( @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, + private val stringConst: StringConst ) { + private var logger: NordicBlekLogger? = null + private val _wheelSize = MutableStateFlow(WheelSizes.default) internal val wheelSize = _wheelSize.asStateFlow() @@ -67,9 +72,6 @@ class CSCRepository @Inject constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() - private val _loggerEvent = simpleSharedFlow() - internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } private var isOnScreen = false @@ -90,6 +92,7 @@ class CSCRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + logger = NordicBlekLogger(context, stringConst.APP_NAME, "CSC", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(CSCService::class.java, device) } @@ -120,7 +123,11 @@ class CSCRepository @Inject constructor( } fun openLogger() { - _loggerEvent.tryEmit(OpenLoggerEvent()) + logger?.launch() + } + + fun log(priority: Int, message: String) { + logger?.log(priority, message) } fun disconnect() { @@ -128,6 +135,7 @@ class CSCRepository @Inject constructor( } private fun clean() { + logger = null _data.value = CSCServiceData() } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 1a5878d6..e631fb42 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -69,9 +69,6 @@ internal class CSCService : NotificationService() { @Inject lateinit var repository: CSCRepository - @Inject - lateinit var stringConst: StringConst - private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -91,13 +88,7 @@ internal class CSCService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicBlekLogger(this@CSCService, stringConst.APP_NAME, "CSC", device.address) - - client = device.connect(this@CSCService, logger = logger) - - repository.loggerEvent - .onEach { logger.launch() } - .launchIn(lifecycleScope) + client = device.connect(this@CSCService, logger = { p, s -> repository.log(p, s) }) client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index c6f9b810..4153883c 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -47,6 +47,7 @@ import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager +import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -54,7 +55,8 @@ import javax.inject.Singleton class HRSRepository @Inject constructor( @ApplicationContext private val context: Context, - private val serviceManager: ServiceManager + private val serviceManager: ServiceManager, + private val stringConst: StringConst ) { private var logger: NordicBlekLogger? = null @@ -64,9 +66,6 @@ class HRSRepository @Inject constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() - private val _loggerEvent = simpleSharedFlow() - internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } private var isOnScreen = false @@ -87,6 +86,7 @@ class HRSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + logger = NordicBlekLogger(context, stringConst.APP_NAME, "HRS", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(HRSService::class.java, device) } @@ -117,7 +117,11 @@ class HRSRepository @Inject constructor( } fun openLogger() { - _loggerEvent.tryEmit(OpenLoggerEvent()) + logger?.launch() + } + + fun log(priority: Int, message: String) { + logger?.log(priority, message) } fun disconnect() { @@ -125,6 +129,7 @@ class HRSRepository @Inject constructor( } private fun clean() { + logger = null _data.value = HRSServiceData() } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 4d1f66a8..b55b4da5 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -53,7 +52,6 @@ import no.nordicsemi.android.kotlin.ble.profile.hrs.BodySensorLocationParser import no.nordicsemi.android.kotlin.ble.profile.hrs.HRSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -71,9 +69,6 @@ internal class HRSService : NotificationService() { @Inject lateinit var repository: HRSRepository - @Inject - lateinit var stringConst: StringConst - private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -93,16 +88,10 @@ internal class HRSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicBlekLogger(this@HRSService, stringConst.APP_NAME, "HRS", device.address) - - client = device.connect(this@HRSService, logger = logger) + client = device.connect(this@HRSService, logger = { p, s -> repository.log(p, s) }) client.waitForBonding() - repository.loggerEvent - .onEach { logger.launch() } - .launchIn(lifecycleScope) - client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 01421843..d795753a 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -38,14 +38,15 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import no.nordicsemi.android.prx.data.PRXServiceData import no.nordicsemi.android.service.DisconnectAndStopEvent -import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager +import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -53,8 +54,10 @@ import javax.inject.Singleton class PRXRepository @Inject internal constructor( @ApplicationContext private val context: Context, - private val serviceManager: ServiceManager + private val serviceManager: ServiceManager, + private val stringConst: StringConst ) { + private var logger: NordicBlekLogger? = null private val _data = MutableStateFlow(PRXServiceData()) internal val data = _data.asStateFlow() @@ -62,9 +65,6 @@ class PRXRepository @Inject internal constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() - private val _loggerEvent = simpleSharedFlow() - internal val loggerEvent = _loggerEvent.asSharedFlow() - private val _remoteAlarmLevel = simpleSharedFlow() internal val remoteAlarmLevel = _remoteAlarmLevel.asSharedFlow() @@ -87,8 +87,8 @@ class PRXRepository @Inject internal constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning - fun launch(device: ServerDevice) { + logger = NordicBlekLogger(context, stringConst.APP_NAME, "PRX", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(PRXService::class.java, device) } @@ -118,7 +118,11 @@ class PRXRepository @Inject internal constructor( } fun openLogger() { - _loggerEvent.tryEmit(OpenLoggerEvent()) + logger?.launch() + } + + fun log(priority: Int, message: String) { + logger?.log(priority, message) } fun onMissingServices() { @@ -132,6 +136,7 @@ class PRXRepository @Inject internal constructor( } private fun clean() { + logger = null _data.value = PRXServiceData() } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index e1226158..d98956e1 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -84,9 +84,6 @@ internal class PRXService : NotificationService() { @Inject lateinit var repository: PRXRepository - @Inject - lateinit var stringConst: StringConst - private lateinit var client: BleGattClient private lateinit var server: BleGattServer @@ -162,16 +159,14 @@ internal class PRXService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicBlekLogger(this@PRXService, stringConst.APP_NAME, "PRX", device.address) - - client = device.connect(this@PRXService, logger = logger, options = BleGattConnectOptions(autoConnect = true)) + client = device.connect( + this@PRXService, + logger = { p, s -> repository.log(p, s) }, + options = BleGattConnectOptions(autoConnect = true) + ) client.waitForBonding() - repository.loggerEvent - .onEach { logger.launch() } - .launchIn(lifecycleScope) - client.connectionStateWithStatus .filterNotNull() .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 672c88ed..2889cb13 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -45,8 +45,8 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.rscs.data.RSCSData import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.service.DisconnectAndStopEvent -import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager +import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -54,7 +54,8 @@ import javax.inject.Singleton class RSCSRepository @Inject constructor( @ApplicationContext private val context: Context, - private val serviceManager: ServiceManager + private val serviceManager: ServiceManager, + private val stringConst: StringConst ) { private var logger: NordicBlekLogger? = null @@ -64,9 +65,6 @@ class RSCSRepository @Inject constructor( private val _stopEvent = simpleSharedFlow() internal val stopEvent = _stopEvent.asSharedFlow() - private val _loggerEvent = simpleSharedFlow() - internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } private var isOnScreen = false @@ -87,6 +85,7 @@ class RSCSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + logger = NordicBlekLogger(context, stringConst.APP_NAME, "RSCS", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(RSCSService::class.java, device) } @@ -109,7 +108,11 @@ class RSCSRepository @Inject constructor( } fun openLogger() { - _loggerEvent.tryEmit(OpenLoggerEvent()) + logger?.launch() + } + + fun log(priority: Int, message: String) { + logger?.log(priority, message) } fun disconnect() { diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index fca9ef0f..92ba9b37 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -69,9 +69,6 @@ internal class RSCSService : NotificationService() { @Inject lateinit var repository: RSCSRepository - @Inject - lateinit var stringConst: StringConst - private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -91,13 +88,7 @@ internal class RSCSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicBlekLogger(this@RSCSService, stringConst.APP_NAME, "RSCS", device.address) - - client = device.connect(this@RSCSService, logger = logger) - - repository.loggerEvent - .onEach { logger.launch() } - .launchIn(lifecycleScope) + client = device.connect(this@RSCSService, logger = { p, s -> repository.log(p, s) }) client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 5e03d40e..dca16a06 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -51,6 +52,7 @@ import no.nordicsemi.android.uart.data.UARTRecord import no.nordicsemi.android.uart.data.UARTRecordType import no.nordicsemi.android.uart.data.UARTServiceData import no.nordicsemi.android.uart.data.parseWithNewLineChar +import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -59,8 +61,11 @@ class UARTRepository @Inject internal constructor( @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val configurationDataSource: ConfigurationDataSource + private val configurationDataSource: ConfigurationDataSource, + private val stringConst: StringConst ) { + private var logger: NordicBlekLogger? = null + private val _data = MutableStateFlow(UARTServiceData()) internal val data = _data.asStateFlow() @@ -70,9 +75,6 @@ class UARTRepository @Inject internal constructor( private val _command = simpleSharedFlow() internal val command = _command.asSharedFlow() - private val _loggerEvent = simpleSharedFlow() - internal val loggerEvent = _loggerEvent.asSharedFlow() - val isRunning = data.map { it.connectionState?.state == GattConnectionState.STATE_CONNECTED } val lastConfigurationName = configurationDataSource.lastConfigurationName @@ -95,6 +97,7 @@ class UARTRepository @Inject internal constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + logger = NordicBlekLogger(context, stringConst.APP_NAME, "UART", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(UARTService::class.java, device) } @@ -131,7 +134,11 @@ class UARTRepository @Inject internal constructor( } fun openLogger() { - _loggerEvent.tryEmit(OpenLoggerEvent()) + logger?.launch() + } + + fun log(priority: Int, message: String) { + logger?.log(priority, message) } fun onMissingServices() { @@ -148,6 +155,7 @@ class UARTRepository @Inject internal constructor( } private fun clean() { + logger = null _data.value = UARTServiceData() } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 432e0554..ad632829 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic @@ -53,7 +52,6 @@ import no.nordicsemi.android.kotlin.ble.core.data.Mtu import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -71,9 +69,6 @@ internal class UARTService : NotificationService() { @Inject lateinit var repository: UARTRepository - @Inject - lateinit var stringConst: StringConst - private lateinit var client: BleGattClient override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -93,16 +88,10 @@ internal class UARTService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val logger = NordicBlekLogger(this@UARTService, stringConst.APP_NAME, "UART", device.address) - - client = device.connect(this@UARTService, logger = logger) + client = device.connect(this@UARTService, logger = { p, s -> repository.log(p, s) }) client.requestMtu(Mtu.max) - repository.loggerEvent - .onEach { logger.launch() } - .launchIn(lifecycleScope) - client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() @@ -114,12 +103,12 @@ internal class UARTService : NotificationService() { client.discoverServices() .filterNotNull() - .onEach { configureGatt(it, logger) } + .onEach { configureGatt(it) } .catch { repository.onMissingServices() } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices, logger: NordicBlekLogger) { + private suspend fun configureGatt(services: BleGattServices) { val uartService = services.findService(UART_SERVICE_UUID)!! val rxCharacteristic = uartService.findCharacteristic(UART_RX_CHARACTERISTIC_UUID)!! val txCharacteristic = uartService.findCharacteristic(UART_TX_CHARACTERISTIC_UUID)!! @@ -134,14 +123,14 @@ internal class UARTService : NotificationService() { txCharacteristic.getNotifications() .onEach { repository.onNewMessageReceived(String(it)) } - .onEach { logger.log(10, "Received: ${String(it)}") } + .onEach { repository.log(10, "Received: ${String(it)}") } .catch { it.printStackTrace() } .launchIn(lifecycleScope) repository.command .onEach { rxCharacteristic.splitWrite(it.toByteArray(), getWriteType(rxCharacteristic)) } .onEach { repository.onNewMessageSent(it) } - .onEach { logger.log(10, "Sent: $it") } + .onEach { repository.log(10, "Sent: $it") } .launchIn(lifecycleScope) } From 0da261b4f34b447daf04c5bc9db86de5f8cf96e0 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 11 May 2023 17:19:22 +0200 Subject: [PATCH 078/101] Bump version catalog --- lib_analytics/build.gradle.kts | 6 +++--- lib_scanner/build.gradle.kts | 2 +- lib_service/build.gradle.kts | 2 +- lib_ui/build.gradle.kts | 2 +- profile_bps/build.gradle.kts | 4 ++-- profile_cgms/build.gradle.kts | 4 ++-- profile_csc/build.gradle.kts | 4 ++-- profile_gls/build.gradle.kts | 4 ++-- profile_hrs/build.gradle.kts | 4 ++-- profile_hts/build.gradle.kts | 4 ++-- profile_prx/build.gradle.kts | 6 +++--- profile_rscs/build.gradle.kts | 4 ++-- profile_uart/build.gradle.kts | 10 +++++----- settings.gradle.kts | 2 +- 14 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib_analytics/build.gradle.kts b/lib_analytics/build.gradle.kts index 5f5d090a..7e2986c8 100644 --- a/lib_analytics/build.gradle.kts +++ b/lib_analytics/build.gradle.kts @@ -38,9 +38,9 @@ android { } dependencies { - implementation(platform("com.google.firebase:firebase-bom:29.2.1")) - implementation("com.google.firebase:firebase-analytics") - implementation("com.google.firebase:firebase-crashlytics") + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.analytics) + implementation(libs.firebase.crashlytics) implementation(libs.nordic.analytics) } diff --git a/lib_scanner/build.gradle.kts b/lib_scanner/build.gradle.kts index 0d4b00fb..90a4d011 100644 --- a/lib_scanner/build.gradle.kts +++ b/lib_scanner/build.gradle.kts @@ -41,7 +41,7 @@ dependencies { implementation(libs.nordic.navigation) implementation(libs.nordic.uiscanner) - implementation("no.nordicsemi.android.kotlin.ble:scanner:0.0.1") + implementation(libs.nordic.blek.scanner) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) diff --git a/lib_service/build.gradle.kts b/lib_service/build.gradle.kts index 784c1105..b0e295c3 100644 --- a/lib_service/build.gradle.kts +++ b/lib_service/build.gradle.kts @@ -44,7 +44,7 @@ dependencies { implementation(libs.nordic.ble.ktx) implementation(libs.nordic.uiscanner) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") + implementation(libs.nordic.blek.core) implementation(libs.androidx.lifecycle.service) implementation(libs.androidx.localbroadcastmanager) diff --git a/lib_ui/build.gradle.kts b/lib_ui/build.gradle.kts index 7fb83133..fd628e96 100644 --- a/lib_ui/build.gradle.kts +++ b/lib_ui/build.gradle.kts @@ -46,5 +46,5 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) implementation(libs.androidx.activity.compose) - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") + implementation(libs.nordic.blek.client) } diff --git a/profile_bps/build.gradle.kts b/profile_bps/build.gradle.kts index 44a19433..06243d7e 100644 --- a/profile_bps/build.gradle.kts +++ b/profile_bps/build.gradle.kts @@ -45,8 +45,8 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.blek.client) + implementation(libs.nordic.blek.profile) implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_cgms/build.gradle.kts b/profile_cgms/build.gradle.kts index b5af38be..fd846bfe 100644 --- a/profile_cgms/build.gradle.kts +++ b/profile_cgms/build.gradle.kts @@ -45,8 +45,8 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.blek.client) + implementation(libs.nordic.blek.profile) implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_csc/build.gradle.kts b/profile_csc/build.gradle.kts index 6549f56a..1fa6038d 100644 --- a/profile_csc/build.gradle.kts +++ b/profile_csc/build.gradle.kts @@ -45,8 +45,8 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.blek.client) + implementation(libs.nordic.blek.profile) implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index 03bd4f2a..254fee01 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -45,8 +45,8 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.blek.client) + implementation(libs.nordic.blek.profile) implementation(libs.chart) diff --git a/profile_hrs/build.gradle.kts b/profile_hrs/build.gradle.kts index 3915e9ac..289a8241 100644 --- a/profile_hrs/build.gradle.kts +++ b/profile_hrs/build.gradle.kts @@ -45,8 +45,8 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.blek.client) + implementation(libs.nordic.blek.profile) implementation(libs.chart) diff --git a/profile_hts/build.gradle.kts b/profile_hts/build.gradle.kts index 2f90cf11..8edf97f1 100644 --- a/profile_hts/build.gradle.kts +++ b/profile_hts/build.gradle.kts @@ -45,8 +45,8 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.blek.client) + implementation(libs.nordic.blek.profile) implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_prx/build.gradle.kts b/profile_prx/build.gradle.kts index e59fb25d..8c93e3b6 100644 --- a/profile_prx/build.gradle.kts +++ b/profile_prx/build.gradle.kts @@ -45,9 +45,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:server:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.blek.client) + implementation(libs.nordic.blek.profile) + implementation(libs.nordic.blek.server) implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_rscs/build.gradle.kts b/profile_rscs/build.gradle.kts index f02e760a..3f3f2a45 100644 --- a/profile_rscs/build.gradle.kts +++ b/profile_rscs/build.gradle.kts @@ -45,8 +45,8 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.blek.client) + implementation(libs.nordic.blek.profile) implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index 17a318aa..1662b6ce 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -32,7 +32,7 @@ plugins { alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) - alias(libs.plugins.kotlin.kapt) + alias(libs.plugins.ksp) alias(libs.plugins.wire) } @@ -51,13 +51,13 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:client:0.0.1") - implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1") + implementation(libs.nordic.blek.client) + implementation(libs.nordic.blek.profile) + implementation(libs.nordic.blek.core) implementation(libs.room.runtime) implementation(libs.room.ktx) - kapt(libs.room.compiler) + ksp(libs.room.compiler) implementation(libs.accompanist.pager) implementation(libs.accompanist.pagerindicators) diff --git a/settings.gradle.kts b/settings.gradle.kts index c39e9e55..524439f4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.4.6") + from("no.nordicsemi.android.gradle:version-catalog:1.5.0") } } } From 4eb74387a40c3afde3e197b9f05b2b368f1bca60 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Thu, 11 May 2023 17:42:57 +0200 Subject: [PATCH 079/101] Remove waitForBonding for BPS profile as it is initiated by notification --- .../java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index ccd99d4d..4c2a5c4b 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -129,8 +129,6 @@ internal class BPSViewModel @Inject constructor( client = device.connect(context, logger = logger) - client.waitForBonding() - client.connectionStateWithStatus .filterNotNull() .onEach { onDataUpdate(it) } From c398146d6b7b1a2b4830e54f33d5302c645426e8 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 12 May 2023 10:27:17 +0200 Subject: [PATCH 080/101] Remove waitForBonding for HTS profile as it is initiated by notification --- .../java/no/nordicsemi/android/hts/repository/HTSService.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 9a0e9cf5..34da4057 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -88,8 +88,6 @@ internal class HTSService : NotificationService() { private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { client = device.connect(this@HTSService, logger = { p, s -> repository.log(p, s) }) - client.waitForBonding() - client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } .filterNotNull() From fffdc0f1e0c3eec774486120125b44f235a8a21f Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 12 May 2023 15:40:08 +0200 Subject: [PATCH 081/101] Bump version catalog --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 524439f4..fd80b33e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.5.0") + from("no.nordicsemi.android.gradle:version-catalog:1.5.1") } } } From 3f42fae2844239adab946a89cc1b60e80b4b7cf6 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Tue, 16 May 2023 10:16:36 +0200 Subject: [PATCH 082/101] Add tests to GLS profile --- .../nrftoolbox/ApplicationScopeModule.kt | 16 ++ .../nrftoolbox/NrfToolboxApplication.kt | 6 + .../toolbox/scanner/ScannerDestination.kt | 2 +- profile_gls/build.gradle.kts | 3 + .../no/nordicsemi/android/gls/GlsServer.kt | 240 ++++++++++++++++++ .../gls/main/viewmodel/GLSViewModel.kt | 16 +- .../nordicsemi/android/gls/ExampleUnitTest.kt | 20 ++ settings.gradle.kts | 8 +- 8 files changed, 298 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/no/nordicsemi/android/nrftoolbox/ApplicationScopeModule.kt create mode 100644 profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt create mode 100644 profile_gls/src/test/java/no/nordicsemi/android/gls/ExampleUnitTest.kt diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/ApplicationScopeModule.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/ApplicationScopeModule.kt new file mode 100644 index 00000000..0e1e58c8 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/ApplicationScopeModule.kt @@ -0,0 +1,16 @@ +package no.nordicsemi.android.nrftoolbox + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob + +@Module +@InstallIn(SingletonComponent::class) +class ApplicationScopeModule { + + @Provides + fun applicationScope() = CoroutineScope(SupervisorJob()) +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt index 05cb85b9..fce2b821 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt @@ -35,6 +35,7 @@ import android.app.Application import dagger.hilt.android.HiltAndroidApp import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.AppOpenEvent +import no.nordicsemi.android.gls.GlsServer import javax.inject.Inject @HiltAndroidApp @@ -43,9 +44,14 @@ class NrfToolboxApplication : Application() { @Inject lateinit var analytics: AppAnalytics + @Inject + lateinit var glsServer: GlsServer + override fun onCreate() { super.onCreate() analytics.logEvent(AppOpenEvent) + + glsServer.start(this) } } diff --git a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt index 8e86d360..2373fc08 100644 --- a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt +++ b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt @@ -21,7 +21,7 @@ val ScannerDestination = defineDestination(ScannerDestinationId) { uuid = arg, onResult = { when (it) { - is DeviceSelected -> navigationViewModel.navigateUpWithResult(ScannerDestinationId, it.device) + is DeviceSelected -> navigationViewModel.navigateUpWithResult(ScannerDestinationId, it.scanResults.device) ScanningCancelled -> navigationViewModel.navigateUp() } } diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index 254fee01..d9f2fae5 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { implementation(libs.nordic.blek.client) implementation(libs.nordic.blek.profile) + implementation(libs.nordic.blek.server) implementation(libs.chart) @@ -63,4 +64,6 @@ dependencies { implementation(libs.androidx.compose.material3) implementation(libs.androidx.activity.compose) implementation(libs.androidx.lifecycle.service) + + testImplementation(libs.junit4) } diff --git a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt new file mode 100644 index 00000000..8054a9ae --- /dev/null +++ b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt @@ -0,0 +1,240 @@ +package no.nordicsemi.android.gls + +import android.annotation.SuppressLint +import android.content.Context +import android.util.Log +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import no.nordicsemi.android.gls.main.viewmodel.BATTERY_LEVEL_CHARACTERISTIC_UUID +import no.nordicsemi.android.gls.main.viewmodel.BATTERY_SERVICE_UUID +import no.nordicsemi.android.gls.main.viewmodel.GLS_SERVICE_UUID +import no.nordicsemi.android.gls.main.viewmodel.GLUCOSE_MEASUREMENT_CHARACTERISTIC +import no.nordicsemi.android.gls.main.viewmodel.GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC +import no.nordicsemi.android.gls.main.viewmodel.RACP_CHARACTERISTIC +import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission +import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty +import no.nordicsemi.android.kotlin.ble.core.ext.toDisplayString +import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser +import no.nordicsemi.android.kotlin.ble.server.main.BleGattServer +import no.nordicsemi.android.kotlin.ble.server.main.service.BleGattServerServiceType +import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristic +import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristicConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattServiceConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.BluetoothGattServerConnection +import javax.inject.Inject +import javax.inject.Singleton + +private const val STANDARD_DELAY = 1000L + +@SuppressLint("MissingPermission") +@Singleton +class GlsServer @Inject constructor( + private val scope: CoroutineScope +) { + + private val records = listOf( + byteArrayOf( + 0x07, + 0x00, + 0x00, + 0xDC.toByte(), + 0x07, + 0x01, + 0x01, + 0x0C, + 0x1E, + 0x05, + 0x00, + 0x00, + 0x26, + 0xD2.toByte(), + 0x11 + ), + byteArrayOf( + 0x07, + 0x01, + 0x00, + 0xDC.toByte(), + 0x07, + 0x01, + 0x01, + 0x0C, + 0x1E, + 0x08, + 0x00, + 0x00, + 0x3D, + 0xD2.toByte(), + 0x11 + ), + byteArrayOf( + 0x07, + 0x02, + 0x00, + 0xDC.toByte(), + 0x07, + 0x01, + 0x01, + 0x0C, + 0x1E, + 0x0B, + 0x00, + 0x00, + 0x54, + 0xD2.toByte(), + 0x11 + ), + byteArrayOf( + 0x07, + 0x03, + 0x00, + 0xDC.toByte(), + 0x07, + 0x01, + 0x01, + 0x0C, + 0x1E, + 0x0E, + 0x00, + 0x00, + 0x6B, + 0xD2.toByte(), + 0x11 + ), + byteArrayOf( + 0x07, + 0x04, + 0x00, + 0xDC.toByte(), + 0x07, + 0x01, + 0x01, + 0x0C, + 0x1E, + 0x11, + 0x00, + 0x00, + 0x82.toByte(), + 0xD2.toByte(), + 0x11 + ) + ) + + private val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01) + + fun start(context: Context) = scope.launch { + val gmCharacteristic = BleServerGattCharacteristicConfig( + GLUCOSE_MEASUREMENT_CHARACTERISTIC, + listOf(BleGattProperty.PROPERTY_NOTIFY), + listOf() + ) + + val gmContextCharacteristic = BleServerGattCharacteristicConfig( + GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC, + listOf(BleGattProperty.PROPERTY_NOTIFY), + listOf() + ) + + val racpCharacteristic = BleServerGattCharacteristicConfig( + RACP_CHARACTERISTIC, + listOf(BleGattProperty.PROPERTY_INDICATE, BleGattProperty.PROPERTY_WRITE), + listOf(BleGattPermission.PERMISSION_WRITE) + ) + + val serviceConfig = BleServerGattServiceConfig( + GLS_SERVICE_UUID, + BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + listOf(gmCharacteristic, gmContextCharacteristic, racpCharacteristic) + ) + + val batteryLevelCharacteristic = BleServerGattCharacteristicConfig( + BATTERY_LEVEL_CHARACTERISTIC_UUID, + listOf(BleGattProperty.PROPERTY_READ, BleGattProperty.PROPERTY_NOTIFY), + listOf(BleGattPermission.PERMISSION_READ) + ) + + val batteryService = BleServerGattServiceConfig( + BATTERY_SERVICE_UUID, + BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + listOf(batteryLevelCharacteristic) + ) + + val server = BleGattServer.create( + context = context, + config = arrayOf(serviceConfig, batteryService), + mock = true + ) + + launch { + server.connections + .mapNotNull { it.values.firstOrNull() } + .collect { setUpConnection(it) } + } + } + + private fun setUpConnection(connection: BluetoothGattServerConnection) { + startGlsService(connection) + startBatteryService(connection) + } + + private fun startGlsService(connection: BluetoothGattServerConnection) { + val glsService = connection.services.findService(GLS_SERVICE_UUID)!! + val glsCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!! + val glsContextCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC)!! + val racpCharacteristic = glsService.findCharacteristic(RACP_CHARACTERISTIC)!! + + racpCharacteristic.value + .filter { it.isNotEmpty() } + .onEach { + if (it.contentEquals(RecordAccessControlPointInputParser.reportAllStoredRecords().value)) { + sendAll(glsCharacteristic) + racpCharacteristic.setValue(racp) + } else if (it.contentEquals(RecordAccessControlPointInputParser.reportLastStoredRecord().value)) { + sendLast(glsCharacteristic) + racpCharacteristic.setValue(racp) + } else if (it.contentEquals(RecordAccessControlPointInputParser.reportFirstStoredRecord().value)) { + sendFirst(glsCharacteristic) + racpCharacteristic.setValue(racp) + } else { + throw IllegalArgumentException("Unknown value") + } + } + .launchIn(scope) + } + + private fun sendFirst(characteristics: BleServerGattCharacteristic) { + characteristics.setValue(records.first()) + } + + private fun sendLast(characteristics: BleServerGattCharacteristic) { + characteristics.setValue(records.last()) + } + + private fun sendAll(characteristics: BleServerGattCharacteristic) = scope.launch { + records.forEach { + characteristics.setValue(it) + delay(STANDARD_DELAY) + } + } + + private fun startBatteryService(connection: BluetoothGattServerConnection) { + val batteryService = connection.services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + scope.launch { + repeat(100) { + batteryLevelCharacteristic.setValue(byteArrayOf(0x61)) + delay(STANDARD_DELAY) + batteryLevelCharacteristic.setValue(byteArrayOf(0x60)) + delay(STANDARD_DELAY) + batteryLevelCharacteristic.setValue(byteArrayOf(0x5F)) + } + } + } +} \ No newline at end of file diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 8a34c0bd..703c4cef 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -87,13 +87,13 @@ import javax.inject.Inject val GLS_SERVICE_UUID: UUID = UUID.fromString("00001808-0000-1000-8000-00805f9b34fb") -private val GM_CHARACTERISTIC = UUID.fromString("00002A18-0000-1000-8000-00805f9b34fb") -private val GM_CONTEXT_CHARACTERISTIC = UUID.fromString("00002A34-0000-1000-8000-00805f9b34fb") -private val GF_CHARACTERISTIC = UUID.fromString("00002A51-0000-1000-8000-00805f9b34fb") -private val RACP_CHARACTERISTIC = UUID.fromString("00002A52-0000-1000-8000-00805f9b34fb") +val GLUCOSE_MEASUREMENT_CHARACTERISTIC = UUID.fromString("00002A18-0000-1000-8000-00805f9b34fb") +val GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC = UUID.fromString("00002A34-0000-1000-8000-00805f9b34fb") +val GLUCOSE_FEATURE_CHARACTERISTIC = UUID.fromString("00002A51-0000-1000-8000-00805f9b34fb") +val RACP_CHARACTERISTIC = UUID.fromString("00002A52-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") +val BATTERY_SERVICE_UUID = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb") +val BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb") @SuppressLint("MissingPermission") @HiltViewModel @@ -202,7 +202,7 @@ internal class GLSViewModel @Inject constructor( private suspend fun configureGatt(services: BleGattServices) { val glsService = services.findService(GLS_SERVICE_UUID)!! - glucoseMeasurementCharacteristic = glsService.findCharacteristic(GM_CHARACTERISTIC)!! + glucoseMeasurementCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!! recordAccessControlPointCharacteristic = glsService.findCharacteristic(RACP_CHARACTERISTIC)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! @@ -219,7 +219,7 @@ internal class GLSViewModel @Inject constructor( .catch { it.printStackTrace() } .launchIn(viewModelScope) - glsService.findCharacteristic(GM_CONTEXT_CHARACTERISTIC)?.getNotifications() + glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC)?.getNotifications() ?.mapNotNull { GlucoseMeasurementContextParser.parse(it) } ?.onEach { _state.value = _state.value.copyWithNewContext(it) } ?.catch { it.printStackTrace() } diff --git a/profile_gls/src/test/java/no/nordicsemi/android/gls/ExampleUnitTest.kt b/profile_gls/src/test/java/no/nordicsemi/android/gls/ExampleUnitTest.kt new file mode 100644 index 00000000..885a5d38 --- /dev/null +++ b/profile_gls/src/test/java/no/nordicsemi/android/gls/ExampleUnitTest.kt @@ -0,0 +1,20 @@ +package no.nordicsemi.android.gls + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + + + + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index fd80b33e..b43ae10f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.5.1") + from("no.nordicsemi.android.gradle:version-catalog:1.5.2") } } } @@ -75,9 +75,9 @@ include(":lib_service") include(":lib_ui") include(":lib_utils") -//if (file("../Android-Common-Libraries").exists()) { -// includeBuild("../Android-Common-Libraries") -//} +if (file("../Android-Common-Libraries").exists()) { + includeBuild("../Android-Common-Libraries") +} if (file("../Kotlin-BLE-Library").exists()) { includeBuild("../Kotlin-BLE-Library") From c299b542068c0745942908b1bd62370b002d923c Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 19 May 2023 11:02:22 +0200 Subject: [PATCH 083/101] Fix Gls server --- .../no/nordicsemi/android/gls/GlsServer.kt | 98 ++----------------- 1 file changed, 8 insertions(+), 90 deletions(-) diff --git a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt index 8054a9ae..1b083268 100644 --- a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt +++ b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt @@ -4,10 +4,10 @@ import android.annotation.SuppressLint import android.content.Context import android.util.Log import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -39,91 +39,11 @@ class GlsServer @Inject constructor( ) { private val records = listOf( - byteArrayOf( - 0x07, - 0x00, - 0x00, - 0xDC.toByte(), - 0x07, - 0x01, - 0x01, - 0x0C, - 0x1E, - 0x05, - 0x00, - 0x00, - 0x26, - 0xD2.toByte(), - 0x11 - ), - byteArrayOf( - 0x07, - 0x01, - 0x00, - 0xDC.toByte(), - 0x07, - 0x01, - 0x01, - 0x0C, - 0x1E, - 0x08, - 0x00, - 0x00, - 0x3D, - 0xD2.toByte(), - 0x11 - ), - byteArrayOf( - 0x07, - 0x02, - 0x00, - 0xDC.toByte(), - 0x07, - 0x01, - 0x01, - 0x0C, - 0x1E, - 0x0B, - 0x00, - 0x00, - 0x54, - 0xD2.toByte(), - 0x11 - ), - byteArrayOf( - 0x07, - 0x03, - 0x00, - 0xDC.toByte(), - 0x07, - 0x01, - 0x01, - 0x0C, - 0x1E, - 0x0E, - 0x00, - 0x00, - 0x6B, - 0xD2.toByte(), - 0x11 - ), - byteArrayOf( - 0x07, - 0x04, - 0x00, - 0xDC.toByte(), - 0x07, - 0x01, - 0x01, - 0x0C, - 0x1E, - 0x11, - 0x00, - 0x00, - 0x82.toByte(), - 0xD2.toByte(), - 0x11 - ) + byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11), + byteArrayOf(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11), + byteArrayOf(0x07, 0x02, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0B, 0x00, 0x00, 0x54, 0xD2.toByte(), 0x11), + byteArrayOf(0x07, 0x03, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0E, 0x00, 0x00, 0x6B, 0xD2.toByte(), 0x11), + byteArrayOf(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11) ) private val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01) @@ -192,7 +112,7 @@ class GlsServer @Inject constructor( racpCharacteristic.value .filter { it.isNotEmpty() } .onEach { - if (it.contentEquals(RecordAccessControlPointInputParser.reportAllStoredRecords().value)) { + if (it.contentEquals(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value)) { sendAll(glsCharacteristic) racpCharacteristic.setValue(racp) } else if (it.contentEquals(RecordAccessControlPointInputParser.reportLastStoredRecord().value)) { @@ -201,8 +121,6 @@ class GlsServer @Inject constructor( } else if (it.contentEquals(RecordAccessControlPointInputParser.reportFirstStoredRecord().value)) { sendFirst(glsCharacteristic) racpCharacteristic.setValue(racp) - } else { - throw IllegalArgumentException("Unknown value") } } .launchIn(scope) @@ -219,7 +137,7 @@ class GlsServer @Inject constructor( private fun sendAll(characteristics: BleServerGattCharacteristic) = scope.launch { records.forEach { characteristics.setValue(it) - delay(STANDARD_DELAY) + delay(100) } } From 6814f1884ab516a16a6c1db8edbc93f41be7285c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Tue, 30 May 2023 14:10:23 +0200 Subject: [PATCH 084/101] Bump version catalog --- .../src/debug/java/no/nordicsemi/android/gls/GlsServer.kt | 1 + settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt index 1b083268..f648779f 100644 --- a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt +++ b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt @@ -152,6 +152,7 @@ class GlsServer @Inject constructor( batteryLevelCharacteristic.setValue(byteArrayOf(0x60)) delay(STANDARD_DELAY) batteryLevelCharacteristic.setValue(byteArrayOf(0x5F)) + delay(STANDARD_DELAY) } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index b43ae10f..1175320e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.5.2") + from("no.nordicsemi.android.gradle:version-catalog:1.5.5") } } } From 69d785ac33d195ee9bae2db6903098ddab49dbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Thu, 1 Jun 2023 14:26:20 +0200 Subject: [PATCH 085/101] Advertise server --- profile_gls/build.gradle.kts | 1 + .../java/no/nordicsemi/android/gls/GlsServer.kt | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index d9f2fae5..af42e035 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -48,6 +48,7 @@ dependencies { implementation(libs.nordic.blek.client) implementation(libs.nordic.blek.profile) implementation(libs.nordic.blek.server) + implementation(libs.nordic.blek.advertiser) implementation(libs.chart) diff --git a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt index f648779f..0b5610f6 100644 --- a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt +++ b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt @@ -2,12 +2,10 @@ package no.nordicsemi.android.gls import android.annotation.SuppressLint import android.content.Context -import android.util.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -17,9 +15,11 @@ import no.nordicsemi.android.gls.main.viewmodel.GLS_SERVICE_UUID import no.nordicsemi.android.gls.main.viewmodel.GLUCOSE_MEASUREMENT_CHARACTERISTIC import no.nordicsemi.android.gls.main.viewmodel.GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC import no.nordicsemi.android.gls.main.viewmodel.RACP_CHARACTERISTIC +import no.nordicsemi.android.kotlin.ble.advertiser.BleAdvertiser +import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertiseConfig +import no.nordicsemi.android.kotlin.ble.core.MockServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty -import no.nordicsemi.android.kotlin.ble.core.ext.toDisplayString import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser import no.nordicsemi.android.kotlin.ble.server.main.BleGattServer import no.nordicsemi.android.kotlin.ble.server.main.service.BleGattServerServiceType @@ -85,12 +85,20 @@ class GlsServer @Inject constructor( listOf(batteryLevelCharacteristic) ) + val device = MockServerDevice( + name = "GLS Server", + address = "55:44:33:22:11" + ) + val server = BleGattServer.create( context = context, config = arrayOf(serviceConfig, batteryService), - mock = true + mock = device ) + val advertiser = BleAdvertiser.create(context) + advertiser.advertise(config = BleAdvertiseConfig(), mock = device).launchIn(scope) + launch { server.connections .mapNotNull { it.values.firstOrNull() } From 8e0348565edb960b3eb58499ca1846a7ed483fa6 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 2 Jun 2023 13:14:52 +0200 Subject: [PATCH 086/101] Fix proguard-rules.pro --- app/proguard-rules.pro | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 40fe7e95..6779d7fc 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,16 +1,18 @@ # Simple XML --keep public class org.simpleframework.** { *; } --keep class org.simpleframework.xml.** { *; } --keep class org.simpleframework.xml.core.** { *; } --keep class org.simpleframework.xml.util.** { *; } +-dontwarn javax.xml.** --keep class no.nordicsemi.android.log.** { *; } +-keep public class org.simpleframework.**{ *; } +-keep class org.simpleframework.xml.**{ *; } +-keep class org.simpleframework.xml.core.**{ *; } +-keep class org.simpleframework.xml.util.**{ *; } --keepattributes ElementList, Root, InnerClasses, LineNumberTable +-keepattributes Signature +-keepattributes *Annotation* --keepclasseswithmembers class * { - @org.simpleframework.xml.* ; +# Ignore our XML Serialization classes +-keep public class your.annotated.pojo.models.*{ + public protected private *; } # Crashlytics From 2a66654c338796e1336b6d50740fc39c77ed7b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Tue, 6 Jun 2023 16:52:27 +0200 Subject: [PATCH 087/101] Add mock tests to GLS --- app/build.gradle.kts | 2 + app/src/main/AndroidManifest.xml | 9 +- .../main/res/xml/data_extraction_rules.xml | 22 +++ .../android/bps/viewmodel/BPSViewModel.kt | 2 +- .../android/cgms/repository/CGMRepository.kt | 2 +- .../android/csc/repository/CSCRepository.kt | 2 +- profile_gls/build.gradle.kts | 7 + .../no/nordicsemi/android/gls/GlsServer.kt | 37 ++-- .../gls/main/viewmodel/GLSViewModel.kt | 16 +- .../nordicsemi/android/gls/ExampleUnitTest.kt | 20 --- .../android/gls/GLSViewModelTest.kt | 158 ++++++++++++++++++ .../android/hrs/service/HRSRepository.kt | 2 +- .../android/hts/repository/HTSRepository.kt | 2 +- .../android/prx/repository/PRXRepository.kt | 2 +- .../android/rscs/repository/RSCSRepository.kt | 2 +- .../android/uart/repository/UARTRepository.kt | 2 +- 16 files changed, 234 insertions(+), 53 deletions(-) create mode 100644 app/src/main/res/xml/data_extraction_rules.xml delete mode 100644 profile_gls/src/test/java/no/nordicsemi/android/gls/ExampleUnitTest.kt create mode 100644 profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cb993657..4959a6eb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -79,4 +79,6 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) implementation(libs.androidx.activity.compose) + + implementation(libs.nordic.blek.client) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 90e9fc2f..05f78497 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,7 +30,8 @@ ~ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> - + @@ -44,12 +45,14 @@ + android:theme="@style/NordicTheme" + android:dataExtractionRules="@xml/data_extraction_rules" + tools:targetApi="s"> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 4c2a5c4b..7f224e51 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -125,7 +125,7 @@ internal class BPSViewModel @Inject constructor( private fun startGattClient(device: ServerDevice) = viewModelScope.launch { _state.value = _state.value.copy(deviceName = device.name) - logger = NordicBlekLogger(context, stringConst.APP_NAME, "BPS", device.address) + logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "BPS", device.address) client = device.connect(context, logger = logger) 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 ec7f73fc..7b144e7f 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 @@ -92,7 +92,7 @@ class CGMRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger(context, stringConst.APP_NAME, "CGM", device.address) + logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "CGM", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(CGMService::class.java, device) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 61750958..62d7242d 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -92,7 +92,7 @@ class CSCRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger(context, stringConst.APP_NAME, "CSC", device.address) + logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "CSC", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(CSCService::class.java, device) } diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index af42e035..40a39bac 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -66,5 +66,12 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.androidx.lifecycle.service) + testImplementation(libs.junit4) + + testImplementation("io.mockk:mockk:1.13.5") + implementation("androidx.test.ext:junit-ktx:1.1.5") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1") + testImplementation("org.slf4j:slf4j-simple:2.0.5") + testImplementation("org.robolectric:robolectric:4.10.3") } diff --git a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt index 0b5610f6..0ca35fc1 100644 --- a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt +++ b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt @@ -38,6 +38,11 @@ class GlsServer @Inject constructor( private val scope: CoroutineScope ) { + lateinit var glsCharacteristic: BleServerGattCharacteristic + lateinit var glsContextCharacteristic: BleServerGattCharacteristic + lateinit var racpCharacteristic: BleServerGattCharacteristic + lateinit var batteryLevelCharacteristic: BleServerGattCharacteristic + private val records = listOf( byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11), byteArrayOf(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11), @@ -48,7 +53,13 @@ class GlsServer @Inject constructor( private val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01) - fun start(context: Context) = scope.launch { + fun start( + context: Context, + device: MockServerDevice = MockServerDevice( + name = "GLS Server", + address = "55:44:33:22:11" + ), + ) = scope.launch { val gmCharacteristic = BleServerGattCharacteristicConfig( GLUCOSE_MEASUREMENT_CHARACTERISTIC, listOf(BleGattProperty.PROPERTY_NOTIFY), @@ -85,11 +96,6 @@ class GlsServer @Inject constructor( listOf(batteryLevelCharacteristic) ) - val device = MockServerDevice( - name = "GLS Server", - address = "55:44:33:22:11" - ) - val server = BleGattServer.create( context = context, config = arrayOf(serviceConfig, batteryService), @@ -107,16 +113,20 @@ class GlsServer @Inject constructor( } private fun setUpConnection(connection: BluetoothGattServerConnection) { + val glsService = connection.services.findService(GLS_SERVICE_UUID)!! + glsCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!! + glsContextCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC)!! + racpCharacteristic = glsService.findCharacteristic(RACP_CHARACTERISTIC)!! + + val batteryService = connection.services.findService(BATTERY_SERVICE_UUID)!! + batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + startGlsService(connection) - startBatteryService(connection) +// startBatteryService(connection) } private fun startGlsService(connection: BluetoothGattServerConnection) { - val glsService = connection.services.findService(GLS_SERVICE_UUID)!! - val glsCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!! - val glsContextCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC)!! - val racpCharacteristic = glsService.findCharacteristic(RACP_CHARACTERISTIC)!! - racpCharacteristic.value .filter { it.isNotEmpty() } .onEach { @@ -150,9 +160,6 @@ class GlsServer @Inject constructor( } private fun startBatteryService(connection: BluetoothGattServerConnection) { - val batteryService = connection.services.findService(BATTERY_SERVICE_UUID)!! - val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! - scope.launch { repeat(100) { batteryLevelCharacteristic.setValue(byteArrayOf(0x61)) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 703c4cef..4fcbbf09 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -105,11 +105,11 @@ internal class GLSViewModel @Inject constructor( private val stringConst: StringConst ) : ViewModel() { - private lateinit var client: BleGattClient + internal lateinit var client: BleGattClient private lateinit var logger: NordicBlekLogger - private lateinit var glucoseMeasurementCharacteristic: BleGattCharacteristic - private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic + internal lateinit var glucoseMeasurementCharacteristic: BleGattCharacteristic + internal lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic private val _state = MutableStateFlow(GLSViewState()) val state = _state.asStateFlow() @@ -117,6 +117,8 @@ internal class GLSViewModel @Inject constructor( private val highestSequenceNumber get() = state.value.glsServiceData.records.keys.maxByOrNull { it.sequenceNumber }?.sequenceNumber ?: -1 + fun test() = 2 + init { navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(GLS_SERVICE_UUID)) @@ -125,7 +127,7 @@ internal class GLSViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + internal fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> onDeviceSelected(result.value) @@ -166,7 +168,7 @@ internal class GLSViewModel @Inject constructor( private fun startGattClient(device: ServerDevice) = viewModelScope.launch { _state.value = _state.value.copy(deviceName = device.name) - logger = NordicBlekLogger(context, stringConst.APP_NAME, "GLS", device.address) + logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "GLS", device.address) client = device.connect(context, logger = logger) @@ -194,7 +196,7 @@ internal class GLSViewModel @Inject constructor( client.disconnect() } - private fun logAnalytics(connectionState: GattConnectionStateWithStatus) { + internal fun logAnalytics(connectionState: GattConnectionStateWithStatus) { if (connectionState.state == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.GLS)) } @@ -318,7 +320,7 @@ internal class GLSViewModel @Inject constructor( try { recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) } catch (e: Exception) { - e.printStackTrace() + _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) } } } diff --git a/profile_gls/src/test/java/no/nordicsemi/android/gls/ExampleUnitTest.kt b/profile_gls/src/test/java/no/nordicsemi/android/gls/ExampleUnitTest.kt deleted file mode 100644 index 885a5d38..00000000 --- a/profile_gls/src/test/java/no/nordicsemi/android/gls/ExampleUnitTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package no.nordicsemi.android.gls - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - - - - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} diff --git a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt new file mode 100644 index 00000000..c76729c2 --- /dev/null +++ b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt @@ -0,0 +1,158 @@ +package no.nordicsemi.android.gls + +import android.content.Context +import io.mockk.coJustRun +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.junit4.MockKRule +import io.mockk.justRun +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.mockkStatic +import io.mockk.spyk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import no.nordicsemi.android.analytics.AppAnalytics +import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.navigation.NavigationResult +import no.nordicsemi.android.common.navigation.Navigator +import no.nordicsemi.android.gls.data.WorkingMode +import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected +import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel +import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.core.MockServerDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus +import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus +import no.nordicsemi.android.ui.view.StringConst +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.shadows.ShadowBluetoothGatt + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(RobolectricTestRunner::class) +internal class GLSViewModelTest { + + @get:Rule + val mockkRule = MockKRule(this) + + @RelaxedMockK + lateinit var navigator: Navigator + + @RelaxedMockK + lateinit var analytics: AppAnalytics + + @MockK + lateinit var stringConst: StringConst + + @RelaxedMockK + lateinit var context: Context + + @RelaxedMockK + lateinit var logger: NordicBlekLogger + + @MockK + lateinit var characteristic: BleGattCharacteristic + + lateinit var viewModel: GLSViewModel + + lateinit var glsServer: GlsServer + + private val device = MockServerDevice( + name = "GLS Server", + address = "55:44:33:22:11" + ) + + @Before + fun setUp() { + Dispatchers.setMain(UnconfinedTestDispatcher()) + } + + @After + fun release() { + Dispatchers.resetMain() + } + + @Before + fun before() { + runBlocking { + viewModel = spyk(GLSViewModel(context, navigator, analytics, stringConst)) + glsServer = GlsServer(CoroutineScope(UnconfinedTestDispatcher())) + glsServer.start(spyk(), device) + } + } + + @Before + fun prepareLogger() { + mockkObject(NordicBlekLogger.Companion) + every { NordicBlekLogger.create(any(), any(), any(), any()) } returns mockk() + } + + @Test + fun addition_isCorrect() { + assertEquals(2, viewModel.test()) + } + + @Test + fun checkOnClick() = runTest { + every { viewModel.recordAccessControlPointCharacteristic } returns characteristic + coJustRun { characteristic.write(any(), any()) } + + viewModel.onEvent(OnWorkingModeSelected(WorkingMode.FIRST)) + + advanceUntilIdle() + assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) + } + + @Test + fun `when connection fails return disconnected`() { + val serverDevice = mockk() + val disconnectedState = GattConnectionStateWithStatus( + GattConnectionState.STATE_DISCONNECTED, + BleGattConnectionStatus.SUCCESS + ) +// every { viewModel.recordAccessControlPointCharacteristic } returns characteristic +// coJustRun { characteristic.write(any(), any()) } + mockkStatic("no.nordicsemi.android.kotlin.ble.client.main.ClientDeviceExtKt") + every { serverDevice.name } returns "Test" + every { serverDevice.address } returns "11:22:33:44:55" + every { stringConst.APP_NAME } returns "Test" + + viewModel.handleResult(NavigationResult.Success(serverDevice)) + + assertEquals(disconnectedState, viewModel.state.value.glsServiceData.connectionState) + } + + @Test + fun checkOnClick2() = runTest { + every { viewModel.recordAccessControlPointCharacteristic } returns characteristic + coJustRun { characteristic.write(any(), any()) } + mockkStatic("no.nordicsemi.android.kotlin.ble.client.main.ClientDeviceExtKt") + every { stringConst.APP_NAME } returns "Test" + justRun { viewModel.logAnalytics(any()) } + + viewModel.handleResult(NavigationResult.Success(device)) + viewModel.onEvent(OnWorkingModeSelected(WorkingMode.FIRST)) + + advanceUntilIdle() + assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) + } +} diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 4153883c..1d8a85f8 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -86,7 +86,7 @@ class HRSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger(context, stringConst.APP_NAME, "HRS", device.address) + logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "HRS", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(HRSService::class.java, device) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index e1714b94..9ef54683 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -87,7 +87,7 @@ class HTSRepository @Inject constructor( fun launch(device: ServerDevice) { _data.value = _data.value.copy(deviceName = device.name) - logger = NordicBlekLogger(context, stringConst.APP_NAME, "HTS", device.address) + logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "HTS", device.address) serviceManager.startService(HTSService::class.java, device) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index d795753a..5d85fc54 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -88,7 +88,7 @@ class PRXRepository @Inject internal constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger(context, stringConst.APP_NAME, "PRX", device.address) + logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "PRX", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(PRXService::class.java, device) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 2889cb13..90adc396 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -85,7 +85,7 @@ class RSCSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger(context, stringConst.APP_NAME, "RSCS", device.address) + logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "RSCS", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(RSCSService::class.java, device) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index dca16a06..e6607dd0 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -97,7 +97,7 @@ class UARTRepository @Inject internal constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger(context, stringConst.APP_NAME, "UART", device.address) + logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "UART", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(UARTService::class.java, device) } From 19c18382aa411de943ff68c16fee5af259eea231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Fri, 16 Jun 2023 16:21:05 +0200 Subject: [PATCH 088/101] Make logger mockable --- .../nrftoolbox/viewmodel/HomeViewModel.kt | 4 +-- .../android/ui/view/NordicLoggerFactory.kt | 10 +++++++ .../no/nordicsemi/android/ui/view/UiModule.kt | 28 +++++++++++++++++++ .../android/csc/repository/CSCRepository.kt | 1 - .../android/csc/repository/CSCService.kt | 3 +- .../gls/main/viewmodel/GLSViewModel.kt | 11 +++++--- .../android/gls/GLSViewModelTest.kt | 15 ++++++++-- .../android/hrs/service/HRSRepository.kt | 1 - .../android/uart/repository/UARTRepository.kt | 1 - 9 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt create mode 100644 lib_ui/src/main/java/no/nordicsemi/android/ui/view/UiModule.kt diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt index c156e5c0..7928ab0f 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt @@ -43,7 +43,7 @@ import kotlinx.coroutines.flow.onEach import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.ProfileOpenEvent import no.nordicsemi.android.cgms.repository.CGMRepository -import no.nordicsemi.android.common.logger.NordicLogger +import no.nordicsemi.android.common.logger.LoggerLauncher import no.nordicsemi.android.common.navigation.DestinationId import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.csc.repository.CSCRepository @@ -114,7 +114,7 @@ class HomeViewModel @Inject constructor( } fun openLogger() { - NordicLogger.launch(context, logger = null) + LoggerLauncher.launch(context) } fun logEvent(event: ProfileOpenEvent) { diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt new file mode 100644 index 00000000..3ffbda8a --- /dev/null +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt @@ -0,0 +1,10 @@ +package no.nordicsemi.android.ui.view + +import android.content.Context +import no.nordicsemi.android.common.logger.BlekLogger +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher + +interface NordicLoggerFactory { + + fun createNordicLogger(context: Context, profile: String?, key: String, name: String?): BlekLoggerAndLauncher +} diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/UiModule.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/UiModule.kt new file mode 100644 index 00000000..62db9285 --- /dev/null +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/UiModule.kt @@ -0,0 +1,28 @@ +package no.nordicsemi.android.ui.view + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.logger.BlekLogger + +@Module +@InstallIn(SingletonComponent::class) +class UiModule { + + @Provides + fun createLogger(): NordicLoggerFactory { + return object : NordicLoggerFactory { + override fun createNordicLogger( + context: Context, + profile: String?, + key: String, + name: String?, + ): BlekLogger { + return NordicBlekLogger.create(context, profile, key, name) + } + } + } +} diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 62d7242d..23f2e7f4 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -48,7 +48,6 @@ import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSize import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSizes import no.nordicsemi.android.service.DisconnectAndStopEvent -import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index e631fb42..50143899 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient import no.nordicsemi.android.kotlin.ble.client.main.connect import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices @@ -52,7 +51,7 @@ import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.csc.CSCDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.android.ui.view.StringConst +import no.nordicsemi.android.ui.view.NordicLoggerFactory import java.util.* import javax.inject.Inject diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 4fcbbf09..1991b513 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -49,7 +49,6 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.GlsDetailsDestinationId @@ -68,6 +67,8 @@ import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus +import no.nordicsemi.android.common.logger.BlekLogger +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementContextParser import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser @@ -81,6 +82,7 @@ import no.nordicsemi.android.kotlin.ble.profile.gls.data.ResponseData import no.nordicsemi.android.kotlin.ble.profile.racp.RACPOpCode import no.nordicsemi.android.kotlin.ble.profile.racp.RACPResponseCode import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId +import no.nordicsemi.android.ui.view.NordicLoggerFactory import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -102,11 +104,12 @@ internal class GLSViewModel @Inject constructor( private val context: Context, private val navigationManager: Navigator, private val analytics: AppAnalytics, - private val stringConst: StringConst + private val stringConst: StringConst, + private val loggerFactory: NordicLoggerFactory ) : ViewModel() { internal lateinit var client: BleGattClient - private lateinit var logger: NordicBlekLogger + private lateinit var logger: BlekLoggerAndLauncher internal lateinit var glucoseMeasurementCharacteristic: BleGattCharacteristic internal lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic @@ -168,7 +171,7 @@ internal class GLSViewModel @Inject constructor( private fun startGattClient(device: ServerDevice) = viewModelScope.launch { _state.value = _state.value.copy(deviceName = device.name) - logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "GLS", device.address) + logger = loggerFactory.createNordicLogger(context, stringConst.APP_NAME, "GLS", device.address) client = device.connect(context, logger = logger) diff --git a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt index c76729c2..f323f0fe 100644 --- a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt +++ b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt @@ -33,6 +33,7 @@ import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus +import no.nordicsemi.android.ui.view.NordicLoggerFactory import no.nordicsemi.android.ui.view.StringConst import org.junit.After import org.junit.Assert.assertEquals @@ -41,7 +42,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import org.robolectric.shadows.ShadowBluetoothGatt /** * Example local unit test, which will execute on the development machine (host). @@ -94,7 +94,18 @@ internal class GLSViewModelTest { @Before fun before() { runBlocking { - viewModel = spyk(GLSViewModel(context, navigator, analytics, stringConst)) + viewModel = spyk(GLSViewModel(context, navigator, analytics, stringConst, object : + NordicLoggerFactory { + override fun createNordicLogger( + context: Context, + profile: String?, + key: String, + name: String?, + ): NordicBlekLogger { + return logger + } + + })) glsServer = GlsServer(CoroutineScope(UnconfinedTestDispatcher())) glsServer.start(spyk(), device) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 1d8a85f8..893aaff5 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -45,7 +45,6 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData import no.nordicsemi.android.service.DisconnectAndStopEvent -import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index e6607dd0..681ea1b1 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -43,7 +43,6 @@ import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.service.DisconnectAndStopEvent -import no.nordicsemi.android.service.OpenLoggerEvent import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.uart.data.ConfigurationDataSource import no.nordicsemi.android.uart.data.MacroEol From bcd481307c5bd862cf0ed886ca264ed681eee02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Sat, 17 Jun 2023 13:28:37 +0200 Subject: [PATCH 089/101] Fix mock engine --- .../ui/view/{UiModule.kt => HiltModule.kt} | 5 ++- .../android/bps/viewmodel/BPSViewModel.kt | 3 +- .../android/cgms/repository/CGMRepository.kt | 3 +- .../android/csc/repository/CSCRepository.kt | 3 +- profile_gls/build.gradle.kts | 4 +- .../no/nordicsemi/android/gls/GlsServer.kt | 6 +-- .../android/gls/main/view/GLSScreen.kt | 1 + .../gls/main/viewmodel/GLSViewModel.kt | 9 ++++- .../android/gls/GLSViewModelTest.kt | 37 +++++++++++-------- .../android/hrs/service/HRSRepository.kt | 3 +- .../android/hts/repository/HTSRepository.kt | 3 +- .../android/prx/repository/PRXRepository.kt | 3 +- .../android/rscs/repository/RSCSRepository.kt | 3 +- .../android/uart/repository/UARTRepository.kt | 3 +- 14 files changed, 54 insertions(+), 32 deletions(-) rename lib_ui/src/main/java/no/nordicsemi/android/ui/view/{UiModule.kt => HiltModule.kt} (85%) diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/UiModule.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/HiltModule.kt similarity index 85% rename from lib_ui/src/main/java/no/nordicsemi/android/ui/view/UiModule.kt rename to lib_ui/src/main/java/no/nordicsemi/android/ui/view/HiltModule.kt index 62db9285..679c69b5 100644 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/UiModule.kt +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/HiltModule.kt @@ -7,10 +7,11 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.common.logger.BlekLogger +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher @Module @InstallIn(SingletonComponent::class) -class UiModule { +class HiltModule { @Provides fun createLogger(): NordicLoggerFactory { @@ -20,7 +21,7 @@ class UiModule { profile: String?, key: String, name: String?, - ): BlekLogger { + ): BlekLoggerAndLauncher { return NordicBlekLogger.create(context, profile, key, name) } } diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 7f224e51..ca44f900 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -53,6 +53,7 @@ import no.nordicsemi.android.bps.view.BPSViewEvent import no.nordicsemi.android.bps.view.BPSViewState import no.nordicsemi.android.bps.view.DisconnectEvent import no.nordicsemi.android.bps.view.OpenLoggerEvent +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator @@ -93,7 +94,7 @@ internal class BPSViewModel @Inject constructor( val state = _state.asStateFlow() private lateinit var client: BleGattClient - private lateinit var logger: NordicBlekLogger + private lateinit var logger: BlekLoggerAndLauncher init { navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(BPS_SERVICE_UUID)) 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 7b144e7f..c9dc5d1d 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 @@ -41,6 +41,7 @@ import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber import no.nordicsemi.android.cgms.data.CGMServiceCommand import no.nordicsemi.android.cgms.data.CGMServiceData import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState @@ -59,7 +60,7 @@ class CGMRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: NordicBlekLogger? = null + private var logger: BlekLoggerAndLauncher? = null private val _data = MutableStateFlow(CGMServiceData()) internal val data = _data.asStateFlow() diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 23f2e7f4..92510765 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit @@ -60,7 +61,7 @@ class CSCRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: NordicBlekLogger? = null + private var logger: BlekLoggerAndLauncher? = null private val _wheelSize = MutableStateFlow(WheelSizes.default) internal val wheelSize = _wheelSize.asStateFlow() diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index 40a39bac..0a1dadb2 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -70,8 +70,8 @@ dependencies { testImplementation(libs.junit4) testImplementation("io.mockk:mockk:1.13.5") - implementation("androidx.test.ext:junit-ktx:1.1.5") - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1") + testImplementation(libs.androidx.test.ext) + testImplementation(libs.kotlinx.coroutines.test) testImplementation("org.slf4j:slf4j-simple:2.0.5") testImplementation("org.robolectric:robolectric:4.10.3") } diff --git a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt index 0ca35fc1..be3cc733 100644 --- a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt +++ b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt @@ -43,7 +43,7 @@ class GlsServer @Inject constructor( lateinit var racpCharacteristic: BleServerGattCharacteristic lateinit var batteryLevelCharacteristic: BleServerGattCharacteristic - private val records = listOf( + val records = listOf( byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11), byteArrayOf(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11), byteArrayOf(0x07, 0x02, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0B, 0x00, 0x00, 0x54, 0xD2.toByte(), 0x11), @@ -51,7 +51,7 @@ class GlsServer @Inject constructor( byteArrayOf(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11) ) - private val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01) + val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01) fun start( context: Context, @@ -123,7 +123,7 @@ class GlsServer @Inject constructor( startGlsService(connection) -// startBatteryService(connection) + startBatteryService(connection) } private fun startGlsService(connection: BluetoothGattServerConnection) { diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index cb5d84e9..a47ebb93 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -31,6 +31,7 @@ package no.nordicsemi.android.gls.main.view +import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 1991b513..a8eddd6b 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -69,6 +69,7 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.common.logger.BlekLogger import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher +import no.nordicsemi.android.kotlin.ble.core.ext.toDisplayString import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementContextParser import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser @@ -111,8 +112,8 @@ internal class GLSViewModel @Inject constructor( internal lateinit var client: BleGattClient private lateinit var logger: BlekLoggerAndLauncher - internal lateinit var glucoseMeasurementCharacteristic: BleGattCharacteristic - internal lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic + private lateinit var glucoseMeasurementCharacteristic: BleGattCharacteristic + private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic private val _state = MutableStateFlow(GLSViewState()) val state = _state.asStateFlow() @@ -238,6 +239,7 @@ internal class GLSViewModel @Inject constructor( } private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) = viewModelScope.launch { + println("AAA: Response code: ${data}") when (data) { is NumberOfRecordsData -> onNumberOfRecordsReceived(data.numberOfRecords) is ResponseData -> when (data.responseCode) { @@ -303,6 +305,7 @@ internal class GLSViewModel @Inject constructor( try { recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) } catch (e: Exception) { + e.printStackTrace() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) } } @@ -313,6 +316,7 @@ internal class GLSViewModel @Inject constructor( try { recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) } catch (e: Exception) { + e.printStackTrace() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) } } @@ -323,6 +327,7 @@ internal class GLSViewModel @Inject constructor( try { recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) } catch (e: Exception) { + e.printStackTrace() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) } } diff --git a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt index f323f0fe..9e8866b7 100644 --- a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt +++ b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt @@ -1,7 +1,6 @@ package no.nordicsemi.android.gls import android.content.Context -import io.mockk.coJustRun import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.RelaxedMockK @@ -13,6 +12,7 @@ import io.mockk.mockkStatic import io.mockk.spyk import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle @@ -26,13 +26,14 @@ import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.data.WorkingMode import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.client.main.ClientScope import no.nordicsemi.android.kotlin.ble.core.MockServerDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus +import no.nordicsemi.android.kotlin.ble.server.main.ServerScope import no.nordicsemi.android.ui.view.NordicLoggerFactory import no.nordicsemi.android.ui.view.StringConst import org.junit.After @@ -40,15 +41,12 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner /** * Example local unit test, which will execute on the development machine (host). * * See [testing documentation](http://d.android.com/tools/testing). */ -@RunWith(RobolectricTestRunner::class) internal class GLSViewModelTest { @get:Rule @@ -69,9 +67,6 @@ internal class GLSViewModelTest { @RelaxedMockK lateinit var logger: NordicBlekLogger - @MockK - lateinit var characteristic: BleGattCharacteristic - lateinit var viewModel: GLSViewModel lateinit var glsServer: GlsServer @@ -94,6 +89,11 @@ internal class GLSViewModelTest { @Before fun before() { runBlocking { + mockkStatic("no.nordicsemi.android.kotlin.ble.client.main.ClientScopeKt") + every { ClientScope } returns CoroutineScope(UnconfinedTestDispatcher()) + mockkStatic("no.nordicsemi.android.kotlin.ble.server.main.ServerScopeKt") + every { ServerScope } returns CoroutineScope(UnconfinedTestDispatcher()) + viewModel = spyk(GLSViewModel(context, navigator, analytics, stringConst, object : NordicLoggerFactory { override fun createNordicLogger( @@ -124,8 +124,8 @@ internal class GLSViewModelTest { @Test fun checkOnClick() = runTest { - every { viewModel.recordAccessControlPointCharacteristic } returns characteristic - coJustRun { characteristic.write(any(), any()) } +// every { viewModel.recordAccessControlPointCharacteristic } returns characteristic +// coJustRun { characteristic.write(any(), any()) } viewModel.onEvent(OnWorkingModeSelected(WorkingMode.FIRST)) @@ -154,16 +154,23 @@ internal class GLSViewModelTest { @Test fun checkOnClick2() = runTest { - every { viewModel.recordAccessControlPointCharacteristic } returns characteristic - coJustRun { characteristic.write(any(), any()) } - mockkStatic("no.nordicsemi.android.kotlin.ble.client.main.ClientDeviceExtKt") every { stringConst.APP_NAME } returns "Test" justRun { viewModel.logAnalytics(any()) } viewModel.handleResult(NavigationResult.Success(device)) - viewModel.onEvent(OnWorkingModeSelected(WorkingMode.FIRST)) advanceUntilIdle() - assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) + delay(1000) + viewModel.onEvent(OnWorkingModeSelected(WorkingMode.FIRST)) + +// advanceUntilIdle() +// assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) +// +//// glsServer.glsCharacteristic.setValue(glsServer.records.first()) +// glsServer.racpCharacteristic.setValue(glsServer.racp) +// +// advanceUntilIdle() +// +// assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus) } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 893aaff5..29db9d17 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -57,7 +58,7 @@ class HRSRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: NordicBlekLogger? = null + private var logger: BlekLoggerAndLauncher? = null private val _data = MutableStateFlow(HRSServiceData()) internal val data = _data.asStateFlow() diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index 9ef54683..db0cedd7 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.hts.view.TemperatureUnit @@ -58,7 +59,7 @@ class HTSRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: NordicBlekLogger? = null + private var logger: BlekLoggerAndLauncher? = null private val _data = MutableStateFlow(HTSServiceData()) internal val data = _data.asStateFlow() diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 5d85fc54..f7ecdcf8 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState @@ -57,7 +58,7 @@ class PRXRepository @Inject internal constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: NordicBlekLogger? = null + private var logger: BlekLoggerAndLauncher? = null private val _data = MutableStateFlow(PRXServiceData()) internal val data = _data.asStateFlow() diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 90adc396..d55efa3e 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState @@ -57,7 +58,7 @@ class RSCSRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: NordicBlekLogger? = null + private var logger: BlekLoggerAndLauncher? = null private val _data = MutableStateFlow(RSCSServiceData()) internal val data = _data.asStateFlow() diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 681ea1b1..c4000fe0 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState @@ -63,7 +64,7 @@ class UARTRepository @Inject internal constructor( private val configurationDataSource: ConfigurationDataSource, private val stringConst: StringConst ) { - private var logger: NordicBlekLogger? = null + private var logger: BlekLoggerAndLauncher? = null private val _data = MutableStateFlow(UARTServiceData()) internal val data = _data.asStateFlow() From dfd307a69821f09ca5d80bc9c3286dabc7ca75da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Mon, 19 Jun 2023 12:58:18 +0200 Subject: [PATCH 090/101] Make mock test working --- profile_gls/build.gradle.kts | 9 +- .../no/nordicsemi/android/gls/GlsServer.kt | 49 ++++++---- .../gls/main/viewmodel/GLSViewModel.kt | 5 +- .../android/gls/GLSViewModelTest.kt | 95 +++++++++++-------- settings.gradle.kts | 2 +- 5 files changed, 97 insertions(+), 63 deletions(-) diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index 0a1dadb2..142d404d 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -66,12 +66,11 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.androidx.lifecycle.service) - testImplementation(libs.junit4) - - testImplementation("io.mockk:mockk:1.13.5") + testImplementation(libs.test.mockk) testImplementation(libs.androidx.test.ext) testImplementation(libs.kotlinx.coroutines.test) - testImplementation("org.slf4j:slf4j-simple:2.0.5") - testImplementation("org.robolectric:robolectric:4.10.3") + testImplementation(libs.test.slf4j.simple) + testImplementation(libs.test.robolectric) + testImplementation(libs.kotlin.junit) } diff --git a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt index be3cc733..53128aa5 100644 --- a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt +++ b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt @@ -38,17 +38,24 @@ class GlsServer @Inject constructor( private val scope: CoroutineScope ) { + lateinit var server: BleGattServer + lateinit var glsCharacteristic: BleServerGattCharacteristic lateinit var glsContextCharacteristic: BleServerGattCharacteristic lateinit var racpCharacteristic: BleServerGattCharacteristic lateinit var batteryLevelCharacteristic: BleServerGattCharacteristic + private var lastRequest = byteArrayOf() + + val YOUNGEST_RECORD = byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11) + val OLDEST_RECORD = byteArrayOf(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11) + val records = listOf( - byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11), + YOUNGEST_RECORD, byteArrayOf(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11), byteArrayOf(0x07, 0x02, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0B, 0x00, 0x00, 0x54, 0xD2.toByte(), 0x11), byteArrayOf(0x07, 0x03, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0E, 0x00, 0x00, 0x6B, 0xD2.toByte(), 0x11), - byteArrayOf(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11) + OLDEST_RECORD ) val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01) @@ -96,7 +103,7 @@ class GlsServer @Inject constructor( listOf(batteryLevelCharacteristic) ) - val server = BleGattServer.create( + server = BleGattServer.create( context = context, config = arrayOf(serviceConfig, batteryService), mock = device @@ -112,6 +119,10 @@ class GlsServer @Inject constructor( } } + internal fun stopServer() { + server.stopServer() + } + private fun setUpConnection(connection: BluetoothGattServerConnection) { val glsService = connection.services.findService(GLS_SERVICE_UUID)!! glsCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!! @@ -123,27 +134,33 @@ class GlsServer @Inject constructor( startGlsService(connection) - startBatteryService(connection) +// startBatteryService(connection) } private fun startGlsService(connection: BluetoothGattServerConnection) { racpCharacteristic.value .filter { it.isNotEmpty() } - .onEach { - if (it.contentEquals(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value)) { - sendAll(glsCharacteristic) - racpCharacteristic.setValue(racp) - } else if (it.contentEquals(RecordAccessControlPointInputParser.reportLastStoredRecord().value)) { - sendLast(glsCharacteristic) - racpCharacteristic.setValue(racp) - } else if (it.contentEquals(RecordAccessControlPointInputParser.reportFirstStoredRecord().value)) { - sendFirst(glsCharacteristic) - racpCharacteristic.setValue(racp) - } - } + .onEach { lastRequest = it } .launchIn(scope) } + internal fun continueWithResponse() { + sendResponse(lastRequest) + } + + private fun sendResponse(request: ByteArray) { + if (request.contentEquals(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value)) { + sendAll(glsCharacteristic) + racpCharacteristic.setValue(racp) + } else if (request.contentEquals(RecordAccessControlPointInputParser.reportLastStoredRecord().value)) { + sendLast(glsCharacteristic) + racpCharacteristic.setValue(racp) + } else if (request.contentEquals(RecordAccessControlPointInputParser.reportFirstStoredRecord().value)) { + sendFirst(glsCharacteristic) + racpCharacteristic.setValue(racp) + } + } + private fun sendFirst(characteristics: BleServerGattCharacteristic) { characteristics.setValue(records.first()) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index a8eddd6b..b9b6235d 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -49,6 +49,7 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.GlsDetailsDestinationId @@ -67,9 +68,6 @@ import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus -import no.nordicsemi.android.common.logger.BlekLogger -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher -import no.nordicsemi.android.kotlin.ble.core.ext.toDisplayString import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementContextParser import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser @@ -239,7 +237,6 @@ internal class GLSViewModel @Inject constructor( } private fun onAccessControlPointDataReceived(data: RecordAccessControlPointData) = viewModelScope.launch { - println("AAA: Response code: ${data}") when (data) { is NumberOfRecordsData -> onNumberOfRecordsReceived(data.numberOfRecords) is ResponseData -> when (data.responseCode) { diff --git a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt index 9e8866b7..c2eb1b56 100644 --- a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt +++ b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt @@ -12,7 +12,6 @@ import io.mockk.mockkStatic import io.mockk.spyk import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle @@ -28,19 +27,21 @@ import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel import no.nordicsemi.android.kotlin.ble.client.main.ClientScope import no.nordicsemi.android.kotlin.ble.core.MockServerDevice -import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus +import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus import no.nordicsemi.android.kotlin.ble.server.main.ServerScope import no.nordicsemi.android.ui.view.NordicLoggerFactory import no.nordicsemi.android.ui.view.StringConst import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test +import kotlin.test.assertContentEquals /** * Example local unit test, which will execute on the development machine (host). @@ -93,6 +94,7 @@ internal class GLSViewModelTest { every { ClientScope } returns CoroutineScope(UnconfinedTestDispatcher()) mockkStatic("no.nordicsemi.android.kotlin.ble.server.main.ServerScopeKt") every { ServerScope } returns CoroutineScope(UnconfinedTestDispatcher()) + every { stringConst.APP_NAME } returns "Test" viewModel = spyk(GLSViewModel(context, navigator, analytics, stringConst, object : NordicLoggerFactory { @@ -106,6 +108,8 @@ internal class GLSViewModelTest { } })) + justRun { viewModel.logAnalytics(any()) } + glsServer = GlsServer(CoroutineScope(UnconfinedTestDispatcher())) glsServer.start(spyk(), device) } @@ -123,54 +127,71 @@ internal class GLSViewModelTest { } @Test - fun checkOnClick() = runTest { -// every { viewModel.recordAccessControlPointCharacteristic } returns characteristic -// coJustRun { characteristic.write(any(), any()) } - - viewModel.onEvent(OnWorkingModeSelected(WorkingMode.FIRST)) - - advanceUntilIdle() - assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) - } - - @Test - fun `when connection fails return disconnected`() { - val serverDevice = mockk() + fun `when connection fails return disconnected`() = runTest { val disconnectedState = GattConnectionStateWithStatus( GattConnectionState.STATE_DISCONNECTED, BleGattConnectionStatus.SUCCESS ) -// every { viewModel.recordAccessControlPointCharacteristic } returns characteristic -// coJustRun { characteristic.write(any(), any()) } - mockkStatic("no.nordicsemi.android.kotlin.ble.client.main.ClientDeviceExtKt") - every { serverDevice.name } returns "Test" - every { serverDevice.address } returns "11:22:33:44:55" - every { stringConst.APP_NAME } returns "Test" + viewModel.handleResult(NavigationResult.Success(device)) + glsServer.stopServer() - viewModel.handleResult(NavigationResult.Success(serverDevice)) + advanceUntilIdle() assertEquals(disconnectedState, viewModel.state.value.glsServiceData.connectionState) } @Test - fun checkOnClick2() = runTest { - every { stringConst.APP_NAME } returns "Test" - justRun { viewModel.logAnalytics(any()) } - + fun `when request first record then change status and get 1 record`() = runTest { viewModel.handleResult(NavigationResult.Success(device)) + advanceUntilIdle() //Needed because of delay() in waitForBonding() + assertEquals(RequestStatus.IDLE, viewModel.state.value.glsServiceData.requestStatus) - advanceUntilIdle() - delay(1000) viewModel.onEvent(OnWorkingModeSelected(WorkingMode.FIRST)) + assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) -// advanceUntilIdle() -// assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) -// -//// glsServer.glsCharacteristic.setValue(glsServer.records.first()) -// glsServer.racpCharacteristic.setValue(glsServer.racp) -// -// advanceUntilIdle() -// -// assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus) + glsServer.continueWithResponse() //continue server breakpoint + + assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus) + assertEquals(1, viewModel.state.value.glsServiceData.records.size) + + val parsedResponse = GlucoseMeasurementParser.parse(glsServer.YOUNGEST_RECORD) + assertEquals(parsedResponse, viewModel.state.value.glsServiceData.records.keys.first()) + } + + @Test + fun `when request last record then change status and get 1 record`() = runTest { + viewModel.handleResult(NavigationResult.Success(device)) + advanceUntilIdle() //Needed because of delay() in waitForBonding() + assertEquals(RequestStatus.IDLE, viewModel.state.value.glsServiceData.requestStatus) + + viewModel.onEvent(OnWorkingModeSelected(WorkingMode.LAST)) + assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) + + glsServer.continueWithResponse() //continue server breakpoint + + assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus) + assertEquals(1, viewModel.state.value.glsServiceData.records.size) + + val parsedResponse = GlucoseMeasurementParser.parse(glsServer.OLDEST_RECORD) + assertEquals(parsedResponse, viewModel.state.value.glsServiceData.records.keys.first()) + } + + @Test + fun `when request all record then change status and get 5 records`() = runTest { + viewModel.handleResult(NavigationResult.Success(device)) + advanceUntilIdle() //Needed because of delay() in waitForBonding() + assertEquals(RequestStatus.IDLE, viewModel.state.value.glsServiceData.requestStatus) + + viewModel.onEvent(OnWorkingModeSelected(WorkingMode.ALL)) + assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) + + glsServer.continueWithResponse() //continue server breakpoint + advanceUntilIdle() //We have to use because of delay() in sendAll() + + assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus) + assertEquals(5, viewModel.state.value.glsServiceData.records.size) + + val expectedRecords = glsServer.records.map { GlucoseMeasurementParser.parse(it) } + assertContentEquals(expectedRecords, viewModel.state.value.glsServiceData.records.keys) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 1175320e..771d7b99 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.5.5") + from("no.nordicsemi.android.gradle:version-catalog:1.5.8") } } } From 2870ef109ef5409c7f05408f1cf264fb80dfdf1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Fri, 23 Jun 2023 10:55:26 +0200 Subject: [PATCH 091/101] Make test with service working --- .../nrftoolbox/NrfToolboxApplication.kt | 53 ++--- .../nrftoolbox/NrfToolboxApplication.kt | 6 - .../android/service/ServiceManager.kt | 34 +-- .../service/ServiceManagerHiltModule.kt | 21 ++ .../android/service/ServiceManagerImpl.kt | 20 ++ lib_ui/build.gradle.kts | 4 + ...le.kt => NordicLoggerFactoryHiltModule.kt} | 3 +- .../gls/main/viewmodel/GLSViewModel.kt | 2 - .../android/gls/GLSViewModelTest.kt | 6 - profile_uart/build.gradle.kts | 21 ++ .../no/nordicsemi/android/gls/UartServer.kt | 184 ++++++++++++++++ .../nordicsemi/android/uart/DaoHiltModule.kt | 20 ++ .../nordicsemi/android/uart/DbHiltModule.kt | 26 +++ .../android/uart/repository/UARTRepository.kt | 7 +- .../android/uart/repository/UARTService.kt | 8 +- .../android/uart/viewmodel/UARTViewModel.kt | 6 +- .../gls/NordicLoggerFactoryTestModule.kt | 40 ++++ .../android/gls/ServiceManagerTestModule.kt | 57 +++++ .../android/gls/TestDbHiltModule.kt | 29 +++ .../nordicsemi/android/gls/TestHiltModule.kt | 13 ++ .../android/gls/UARTViewModelTest.kt | 208 ++++++++++++++++++ 21 files changed, 682 insertions(+), 86 deletions(-) rename profile_uart/src/main/java/no/nordicsemi/android/uart/HiltModule.kt => app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt (62%) rename app/src/{main => release}/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt (94%) create mode 100644 lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerHiltModule.kt create mode 100644 lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerImpl.kt rename lib_ui/src/main/java/no/nordicsemi/android/ui/view/{HiltModule.kt => NordicLoggerFactoryHiltModule.kt} (91%) create mode 100644 profile_uart/src/debug/java/no/nordicsemi/android/gls/UartServer.kt create mode 100644 profile_uart/src/main/java/no/nordicsemi/android/uart/DaoHiltModule.kt create mode 100644 profile_uart/src/main/java/no/nordicsemi/android/uart/DbHiltModule.kt create mode 100644 profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt create mode 100644 profile_uart/src/test/java/no/nordicsemi/android/gls/ServiceManagerTestModule.kt create mode 100644 profile_uart/src/test/java/no/nordicsemi/android/gls/TestDbHiltModule.kt create mode 100644 profile_uart/src/test/java/no/nordicsemi/android/gls/TestHiltModule.kt create mode 100644 profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/HiltModule.kt b/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt similarity index 62% rename from profile_uart/src/main/java/no/nordicsemi/android/uart/HiltModule.kt rename to app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt index e3a8dcc3..7919cd5d 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/HiltModule.kt +++ b/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt @@ -29,37 +29,32 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.uart +package no.nordicsemi.android.nrftoolbox -import android.content.Context -import androidx.room.Room -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import no.nordicsemi.android.uart.db.ConfigurationsDao -import no.nordicsemi.android.uart.db.ConfigurationsDatabase -import no.nordicsemi.android.uart.db.MIGRATION_1_2 -import javax.inject.Singleton +import android.app.Application +import dagger.hilt.android.HiltAndroidApp +import no.nordicsemi.android.analytics.AppAnalytics +import no.nordicsemi.android.analytics.AppOpenEvent +import no.nordicsemi.android.gls.UartServer +import javax.inject.Inject -@Module -@InstallIn(SingletonComponent::class) -class HiltModule { +@HiltAndroidApp +class NrfToolboxApplication : Application() { - @Provides - @Singleton - internal fun provideDB(@ApplicationContext context: Context): ConfigurationsDatabase { - return Room.databaseBuilder( - context, - ConfigurationsDatabase::class.java, "toolbox_uart.db" - ).addMigrations(MIGRATION_1_2).build() + @Inject + lateinit var analytics: AppAnalytics + + @Inject + lateinit var glsServer: UartServer + + @Inject + lateinit var uartServer: UartServer + + override fun onCreate() { + super.onCreate() + + analytics.logEvent(AppOpenEvent) + + uartServer.start(this) } - - @Provides - @Singleton - internal fun provideDao(db: ConfigurationsDatabase): ConfigurationsDao { - return db.dao() - } - } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt b/app/src/release/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt similarity index 94% rename from app/src/main/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt rename to app/src/release/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt index fce2b821..05cb85b9 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt +++ b/app/src/release/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt @@ -35,7 +35,6 @@ import android.app.Application import dagger.hilt.android.HiltAndroidApp import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.AppOpenEvent -import no.nordicsemi.android.gls.GlsServer import javax.inject.Inject @HiltAndroidApp @@ -44,14 +43,9 @@ class NrfToolboxApplication : Application() { @Inject lateinit var analytics: AppAnalytics - @Inject - lateinit var glsServer: GlsServer - override fun onCreate() { super.onCreate() analytics.logEvent(AppOpenEvent) - - glsServer.start(this) } } diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt index faf0c063..c9ae50fc 100644 --- a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt +++ b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt @@ -31,41 +31,11 @@ package no.nordicsemi.android.service -import android.bluetooth.BluetoothDevice -import android.content.Context -import android.content.Intent -import dagger.hilt.android.qualifiers.ApplicationContext import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import javax.inject.Inject const val DEVICE_DATA = "device-data" -class ServiceManager @Inject constructor( - @ApplicationContext - private val context: Context -) { +interface ServiceManager { - fun startService(service: Class, device: ServerDevice) { - val intent = Intent(context, service).apply { - putExtra(DEVICE_DATA, device) - } - context.startService(intent) - } - - fun startService(service: Class, device: BluetoothDevice) { - val intent = Intent(context, service).apply { - putExtra(DEVICE_DATA, device) - } - context.startService(intent) - } - - fun startService(service: Class) { - val intent = Intent(context, service) - context.startService(intent) - } - - fun stopService(service: Class) { - val intent = Intent(context, service) - context.stopService(intent) - } + fun startService(service: Class, device: ServerDevice) } diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerHiltModule.kt b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerHiltModule.kt new file mode 100644 index 00000000..b9b0eba9 --- /dev/null +++ b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerHiltModule.kt @@ -0,0 +1,21 @@ +package no.nordicsemi.android.service + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +class ServiceManagerHiltModule { + + @Provides + fun createServiceManager( + @ApplicationContext + context: Context, + ): ServiceManager { + return ServiceManagerImpl(context) + } +} diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerImpl.kt b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerImpl.kt new file mode 100644 index 00000000..1872a84d --- /dev/null +++ b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerImpl.kt @@ -0,0 +1,20 @@ +package no.nordicsemi.android.service + +import android.content.Context +import android.content.Intent +import dagger.hilt.android.qualifiers.ApplicationContext +import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import javax.inject.Inject + +class ServiceManagerImpl @Inject constructor( + @ApplicationContext + private val context: Context +): ServiceManager { + + override fun startService(service: Class, device: ServerDevice) { + val intent = Intent(context, service).apply { + putExtra(DEVICE_DATA, device) + } + context.startService(intent) + } +} diff --git a/lib_ui/build.gradle.kts b/lib_ui/build.gradle.kts index fd628e96..e8a0ff7a 100644 --- a/lib_ui/build.gradle.kts +++ b/lib_ui/build.gradle.kts @@ -35,6 +35,10 @@ plugins { android { namespace = "no.nordicsemi.android.ui" + + testOptions { + unitTests.isIncludeAndroidResources = true + } } dependencies { diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/HiltModule.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt similarity index 91% rename from lib_ui/src/main/java/no/nordicsemi/android/ui/view/HiltModule.kt rename to lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt index 679c69b5..8550b29d 100644 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/HiltModule.kt +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt @@ -6,12 +6,11 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import no.nordicsemi.android.common.logger.NordicBlekLogger -import no.nordicsemi.android.common.logger.BlekLogger import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher @Module @InstallIn(SingletonComponent::class) -class HiltModule { +class NordicLoggerFactoryHiltModule { @Provides fun createLogger(): NordicLoggerFactory { diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index b9b6235d..09a3352c 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -119,8 +119,6 @@ internal class GLSViewModel @Inject constructor( private val highestSequenceNumber get() = state.value.glsServiceData.records.keys.maxByOrNull { it.sequenceNumber }?.sequenceNumber ?: -1 - fun test() = 2 - init { navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(GLS_SERVICE_UUID)) diff --git a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt index c2eb1b56..22f4c8ed 100644 --- a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt +++ b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt @@ -37,7 +37,6 @@ import no.nordicsemi.android.ui.view.NordicLoggerFactory import no.nordicsemi.android.ui.view.StringConst import org.junit.After import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -121,11 +120,6 @@ internal class GLSViewModelTest { every { NordicBlekLogger.create(any(), any(), any(), any()) } returns mockk() } - @Test - fun addition_isCorrect() { - assertEquals(2, viewModel.test()) - } - @Test fun `when connection fails return disconnected`() = runTest { val disconnectedState = GattConnectionStateWithStatus( diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index 1662b6ce..bd8230c1 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -38,6 +38,10 @@ plugins { android { namespace = "no.nordicsemi.android.uart" + + testOptions { + unitTests.isIncludeAndroidResources = true + } } wire { @@ -54,6 +58,8 @@ dependencies { implementation(libs.nordic.blek.client) implementation(libs.nordic.blek.profile) implementation(libs.nordic.blek.core) + implementation(libs.nordic.blek.server) + implementation(libs.nordic.blek.advertiser) implementation(libs.room.runtime) implementation(libs.room.ktx) @@ -81,6 +87,21 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.androidx.lifecycle.service) + // For Robolectric tests. + testImplementation("com.google.dagger:hilt-android-testing:2.44") + // ...with Kotlin. + kaptTest("com.google.dagger:hilt-android-compiler:2.46.1") + + testImplementation("androidx.test:rules:1.5.0") + + testImplementation(libs.junit4) + testImplementation(libs.test.mockk) + testImplementation(libs.androidx.test.ext) + testImplementation(libs.kotlinx.coroutines.test) + testImplementation(libs.test.slf4j.simple) + testImplementation(libs.test.robolectric) + testImplementation(libs.kotlin.junit) + implementation("org.simpleframework:simple-xml:2.7.1") { exclude(group = "stax", module = "stax-api") exclude(group = "xpp3", module = "xpp3") diff --git a/profile_uart/src/debug/java/no/nordicsemi/android/gls/UartServer.kt b/profile_uart/src/debug/java/no/nordicsemi/android/gls/UartServer.kt new file mode 100644 index 00000000..bf041074 --- /dev/null +++ b/profile_uart/src/debug/java/no/nordicsemi/android/gls/UartServer.kt @@ -0,0 +1,184 @@ +package no.nordicsemi.android.gls + +import android.annotation.SuppressLint +import android.content.Context +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import no.nordicsemi.android.kotlin.ble.advertiser.BleAdvertiser +import no.nordicsemi.android.kotlin.ble.core.MockServerDevice +import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertiseConfig +import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission +import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty +import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser +import no.nordicsemi.android.kotlin.ble.server.main.BleGattServer +import no.nordicsemi.android.kotlin.ble.server.main.service.BleGattServerServiceType +import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristic +import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristicConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattServiceConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.BluetoothGattServerConnection +import no.nordicsemi.android.uart.repository.BATTERY_LEVEL_CHARACTERISTIC_UUID +import no.nordicsemi.android.uart.repository.BATTERY_SERVICE_UUID +import no.nordicsemi.android.uart.repository.UART_RX_CHARACTERISTIC_UUID +import no.nordicsemi.android.uart.repository.UART_SERVICE_UUID +import no.nordicsemi.android.uart.repository.UART_TX_CHARACTERISTIC_UUID +import javax.inject.Inject +import javax.inject.Singleton + +private const val STANDARD_DELAY = 1000L + +@SuppressLint("MissingPermission") +@Singleton +class UartServer @Inject constructor( + private val scope: CoroutineScope +) { + + lateinit var server: BleGattServer + + lateinit var glsCharacteristic: BleServerGattCharacteristic + lateinit var glsContextCharacteristic: BleServerGattCharacteristic + lateinit var racpCharacteristic: BleServerGattCharacteristic + lateinit var batteryLevelCharacteristic: BleServerGattCharacteristic + + private var lastRequest = byteArrayOf() + + val YOUNGEST_RECORD = byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11) + val OLDEST_RECORD = byteArrayOf(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11) + + val records = listOf( + YOUNGEST_RECORD, + byteArrayOf(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11), + byteArrayOf(0x07, 0x02, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0B, 0x00, 0x00, 0x54, 0xD2.toByte(), 0x11), + byteArrayOf(0x07, 0x03, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0E, 0x00, 0x00, 0x6B, 0xD2.toByte(), 0x11), + OLDEST_RECORD + ) + + val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01) + + fun start( + context: Context, + device: MockServerDevice = MockServerDevice( + name = "GLS Server", + address = "55:44:33:22:11" + ), + ) = scope.launch { + val rxCharacteristic = BleServerGattCharacteristicConfig( + UART_RX_CHARACTERISTIC_UUID, + listOf(BleGattProperty.PROPERTY_NOTIFY), + listOf() + ) + + val txCharacteristic = BleServerGattCharacteristicConfig( + UART_TX_CHARACTERISTIC_UUID, + listOf(BleGattProperty.PROPERTY_INDICATE, BleGattProperty.PROPERTY_WRITE), + listOf(BleGattPermission.PERMISSION_WRITE) + ) + + val uartService = BleServerGattServiceConfig( + UART_SERVICE_UUID, + BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + listOf(rxCharacteristic, txCharacteristic) + ) + + val batteryLevelCharacteristic = BleServerGattCharacteristicConfig( + BATTERY_LEVEL_CHARACTERISTIC_UUID, + listOf(BleGattProperty.PROPERTY_READ, BleGattProperty.PROPERTY_NOTIFY), + listOf(BleGattPermission.PERMISSION_READ) + ) + + val batteryService = BleServerGattServiceConfig( + BATTERY_SERVICE_UUID, + BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + listOf(batteryLevelCharacteristic) + ) + + server = BleGattServer.create( + context = context, + config = arrayOf(uartService, batteryService), + mock = device + ) + + val advertiser = BleAdvertiser.create(context) + advertiser.advertise(config = BleAdvertiseConfig(), mock = device).launchIn(scope) + + launch { + server.connections + .mapNotNull { it.values.firstOrNull() } + .collect { setUpConnection(it) } + } + } + + internal fun stopServer() { + server.stopServer() + } + + private fun setUpConnection(connection: BluetoothGattServerConnection) { +// val glsService = connection.services.findService(GLS_SERVICE_UUID)!! +// glsCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!! +// glsContextCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC)!! +// racpCharacteristic = glsService.findCharacteristic(RACP_CHARACTERISTIC)!! + + val batteryService = connection.services.findService(BATTERY_SERVICE_UUID)!! + batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + +// startGlsService(connection) +// startBatteryService(connection) + } + + private fun startGlsService(connection: BluetoothGattServerConnection) { + racpCharacteristic.value + .filter { it.isNotEmpty() } + .onEach { lastRequest = it } + .launchIn(scope) + } + + internal fun continueWithResponse() { + sendResponse(lastRequest) + } + + private fun sendResponse(request: ByteArray) { + if (request.contentEquals(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value)) { + sendAll(glsCharacteristic) + racpCharacteristic.setValue(racp) + } else if (request.contentEquals(RecordAccessControlPointInputParser.reportLastStoredRecord().value)) { + sendLast(glsCharacteristic) + racpCharacteristic.setValue(racp) + } else if (request.contentEquals(RecordAccessControlPointInputParser.reportFirstStoredRecord().value)) { + sendFirst(glsCharacteristic) + racpCharacteristic.setValue(racp) + } + } + + private fun sendFirst(characteristics: BleServerGattCharacteristic) { + characteristics.setValue(records.first()) + } + + private fun sendLast(characteristics: BleServerGattCharacteristic) { + characteristics.setValue(records.last()) + } + + private fun sendAll(characteristics: BleServerGattCharacteristic) = scope.launch { + records.forEach { + characteristics.setValue(it) + delay(100) + } + } + + private fun startBatteryService(connection: BluetoothGattServerConnection) { + scope.launch { + repeat(100) { + batteryLevelCharacteristic.setValue(byteArrayOf(0x61)) + delay(STANDARD_DELAY) + batteryLevelCharacteristic.setValue(byteArrayOf(0x60)) + delay(STANDARD_DELAY) + batteryLevelCharacteristic.setValue(byteArrayOf(0x5F)) + delay(STANDARD_DELAY) + } + } + } +} \ No newline at end of file diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/DaoHiltModule.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/DaoHiltModule.kt new file mode 100644 index 00000000..68b70208 --- /dev/null +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/DaoHiltModule.kt @@ -0,0 +1,20 @@ +package no.nordicsemi.android.uart + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import no.nordicsemi.android.uart.db.ConfigurationsDao +import no.nordicsemi.android.uart.db.ConfigurationsDatabase +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class DaoHiltModule { + + @Provides + @Singleton + internal fun provideDao(db: ConfigurationsDatabase): ConfigurationsDao { + return db.dao() + } +} diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/DbHiltModule.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/DbHiltModule.kt new file mode 100644 index 00000000..327198d4 --- /dev/null +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/DbHiltModule.kt @@ -0,0 +1,26 @@ +package no.nordicsemi.android.uart + +import android.content.Context +import androidx.room.Room +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import no.nordicsemi.android.uart.db.ConfigurationsDatabase +import no.nordicsemi.android.uart.db.MIGRATION_1_2 +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class DbHiltModule { + + @Provides + @Singleton + internal fun provideDB(@ApplicationContext context: Context): ConfigurationsDatabase { + return Room.databaseBuilder( + context, + ConfigurationsDatabase::class.java, "toolbox_uart.db" + ).addMigrations(MIGRATION_1_2).build() + } +} diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index c4000fe0..961ef4b4 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -39,7 +39,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher -import no.nordicsemi.android.common.logger.NordicBlekLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -52,6 +51,7 @@ import no.nordicsemi.android.uart.data.UARTRecord import no.nordicsemi.android.uart.data.UARTRecordType import no.nordicsemi.android.uart.data.UARTServiceData import no.nordicsemi.android.uart.data.parseWithNewLineChar +import no.nordicsemi.android.ui.view.NordicLoggerFactory import no.nordicsemi.android.ui.view.StringConst import javax.inject.Inject import javax.inject.Singleton @@ -62,7 +62,8 @@ class UARTRepository @Inject internal constructor( private val context: Context, private val serviceManager: ServiceManager, private val configurationDataSource: ConfigurationDataSource, - private val stringConst: StringConst + private val stringConst: StringConst, + private val loggerFactory: NordicLoggerFactory ) { private var logger: BlekLoggerAndLauncher? = null @@ -97,7 +98,7 @@ class UARTRepository @Inject internal constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "UART", device.address) + logger = loggerFactory.createNordicLogger(context, stringConst.APP_NAME, "UART", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(UARTService::class.java, device) } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index ad632829..40812a05 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -56,11 +56,11 @@ import java.util.* import javax.inject.Inject val UART_SERVICE_UUID: UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E") -private val UART_RX_CHARACTERISTIC_UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E") -private val UART_TX_CHARACTERISTIC_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E") +internal val UART_RX_CHARACTERISTIC_UUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E") +internal val UART_TX_CHARACTERISTIC_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E") -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 val BATTERY_SERVICE_UUID = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb") +internal val BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb") @SuppressLint("MissingPermission") @AndroidEntryPoint diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index 17cd4cff..c528ef9d 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -77,6 +77,7 @@ import no.nordicsemi.android.uart.view.OnRunMacro import no.nordicsemi.android.uart.view.OpenLogger import no.nordicsemi.android.uart.view.UARTViewEvent import no.nordicsemi.android.uart.view.UARTViewState +import no.nordicsemi.android.ui.view.NordicLoggerFactory import javax.inject.Inject @HiltViewModel @@ -84,7 +85,8 @@ internal class UARTViewModel @Inject constructor( private val repository: UARTRepository, private val navigationManager: Navigator, private val dataSource: UARTPersistentDataSource, - private val analytics: AppAnalytics + private val analytics: AppAnalytics, + private val loggerFactory: NordicLoggerFactory ) : ViewModel() { private val _state = MutableStateFlow(UARTViewState()) @@ -126,7 +128,7 @@ internal class UARTViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun handleResult(result: NavigationResult) { + internal fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Success -> onDeviceSelected(result.value) diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt new file mode 100644 index 00000000..18a53331 --- /dev/null +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt @@ -0,0 +1,40 @@ +package no.nordicsemi.android.gls + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.components.SingletonComponent +import dagger.hilt.testing.TestInstallIn +import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher +import no.nordicsemi.android.ui.view.NordicLoggerFactory +import no.nordicsemi.android.ui.view.NordicLoggerFactoryHiltModule + +@Module +@TestInstallIn( + components = [SingletonComponent::class], + replaces = [NordicLoggerFactoryHiltModule::class] +) +class NordicLoggerFactoryTestModule { + + @Provides + fun createLogger(): NordicLoggerFactory { + return object : NordicLoggerFactory { + override fun createNordicLogger( + context: Context, + profile: String?, + key: String, + name: String?, + ): BlekLoggerAndLauncher { + return object : BlekLoggerAndLauncher { + override fun launch() { + + } + + override fun log(priority: Int, log: String) { + println(log) + } + } + } + } + } +} diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/ServiceManagerTestModule.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/ServiceManagerTestModule.kt new file mode 100644 index 00000000..70b59201 --- /dev/null +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/ServiceManagerTestModule.kt @@ -0,0 +1,57 @@ +package no.nordicsemi.android.gls + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import dagger.Module +import dagger.Provides +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import dagger.hilt.testing.TestInstallIn +import no.nordicsemi.android.kotlin.ble.core.MockServerDevice +import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.service.DEVICE_DATA +import no.nordicsemi.android.service.ServiceManager +import no.nordicsemi.android.service.ServiceManagerHiltModule +import no.nordicsemi.android.uart.repository.UARTService +import org.robolectric.Robolectric +import org.robolectric.android.controller.ServiceController +import javax.inject.Singleton + +@Module +@TestInstallIn( + components = [SingletonComponent::class], + replaces = [ServiceManagerHiltModule::class] +) +class ServiceManagerTestModule { + + private val componentName = ComponentName("org.robolectric", UARTService::class.java.name) + + @Provides + internal fun provideDevice(): MockServerDevice { + return MockServerDevice( + name = "GLS Server", + address = "55:44:33:22:11" + ) + } + + @Provides + internal fun provideServiceController( + @ApplicationContext context: Context, + device: MockServerDevice + ): ServiceController { + return Robolectric.buildService(UARTService::class.java, Intent(context, UARTService::class.java).apply { + putExtra(DEVICE_DATA, device) + }) + } + + @Provides + @Singleton + internal fun provideServiceManager(controller: ServiceController): ServiceManager { + return object : ServiceManager { + override fun startService(service: Class, device: ServerDevice) { + controller.create().startCommand(3, 4).get() + } + } + } +} diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/TestDbHiltModule.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/TestDbHiltModule.kt new file mode 100644 index 00000000..3dab6188 --- /dev/null +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/TestDbHiltModule.kt @@ -0,0 +1,29 @@ +package no.nordicsemi.android.gls + +import android.content.Context +import androidx.room.Room +import dagger.Module +import dagger.Provides +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import dagger.hilt.testing.TestInstallIn +import no.nordicsemi.android.uart.DbHiltModule +import no.nordicsemi.android.uart.db.ConfigurationsDatabase +import no.nordicsemi.android.uart.db.MIGRATION_1_2 +import javax.inject.Singleton + +@Module +@TestInstallIn( + components = [SingletonComponent::class], + replaces = [DbHiltModule::class] +) +class TestDbHiltModule { + @Provides + @Singleton + internal fun provideDB(@ApplicationContext context: Context): ConfigurationsDatabase { + return Room.inMemoryDatabaseBuilder( + context, + ConfigurationsDatabase::class.java + ).addMigrations(MIGRATION_1_2).build() + } +} \ No newline at end of file diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/TestHiltModule.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/TestHiltModule.kt new file mode 100644 index 00000000..36e316cb --- /dev/null +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/TestHiltModule.kt @@ -0,0 +1,13 @@ +package no.nordicsemi.android.gls + +import dagger.Module +import dagger.hilt.components.SingletonComponent +import dagger.hilt.testing.TestInstallIn + +//@Module +//@TestInstallIn( +// components = [SingletonComponent::class], +// replaces = [AnalyticsModule::class] +//) +//class TestHiltModule { +//} diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt new file mode 100644 index 00000000..fcedf25a --- /dev/null +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt @@ -0,0 +1,208 @@ +package no.nordicsemi.android.gls + +import android.content.Context +import androidx.test.rule.ServiceTestRule +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.HiltTestApplication +import dagger.hilt.android.testing.UninstallModules +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.junit4.MockKRule +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.mockkStatic +import io.mockk.spyk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import no.nordicsemi.android.analytics.AppAnalytics +import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.navigation.NavigationResult +import no.nordicsemi.android.common.navigation.Navigator +import no.nordicsemi.android.common.navigation.di.NavigationModule +import no.nordicsemi.android.kotlin.ble.client.main.ClientScope +import no.nordicsemi.android.kotlin.ble.core.MockServerDevice +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus +import no.nordicsemi.android.kotlin.ble.server.main.ServerScope +import no.nordicsemi.android.uart.data.UARTPersistentDataSource +import no.nordicsemi.android.uart.repository.UARTRepository +import no.nordicsemi.android.uart.view.DisconnectEvent +import no.nordicsemi.android.uart.viewmodel.UARTViewModel +import no.nordicsemi.android.ui.view.NordicLoggerFactory +import no.nordicsemi.android.ui.view.StringConst +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import javax.inject.Inject + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@HiltAndroidTest +@Config(application = HiltTestApplication::class) +@UninstallModules(NavigationModule::class) +@RunWith(RobolectricTestRunner::class) +internal class UARTViewModelTest { + + @get:Rule + val mockkRule = MockKRule(this) + + @get:Rule + val serviceRule = ServiceTestRule() + + @get:Rule + var hiltRule = HiltAndroidRule(this) + + @BindValue + @JvmField + val analyticsService: Navigator = mockk(relaxed = true) + + @RelaxedMockK + lateinit var analytics: AppAnalytics + + @MockK + lateinit var stringConst: StringConst + + @RelaxedMockK + lateinit var context: Context + + @RelaxedMockK + lateinit var logger: NordicBlekLogger + + @Inject + lateinit var repository: UARTRepository + + @Inject + lateinit var dataSource: UARTPersistentDataSource + + lateinit var viewModel: UARTViewModel + + lateinit var uartServer: UartServer + + @Inject + lateinit var device: MockServerDevice + + @Before + fun setUp() { + hiltRule.inject() + Dispatchers.setMain(UnconfinedTestDispatcher()) + } + + @After + fun release() { + Dispatchers.resetMain() + } + + @Before + fun before() { + viewModel = UARTViewModel(repository, mockk(relaxed = true), dataSource, mockk(relaxed = true), object : + NordicLoggerFactory { + override fun createNordicLogger( + context: Context, + profile: String?, + key: String, + name: String?, + ): NordicBlekLogger { + return logger + } + + }) + runBlocking { + mockkStatic("no.nordicsemi.android.kotlin.ble.client.main.ClientScopeKt") + every { ClientScope } returns CoroutineScope(UnconfinedTestDispatcher()) + mockkStatic("no.nordicsemi.android.kotlin.ble.server.main.ServerScopeKt") + every { ServerScope } returns CoroutineScope(UnconfinedTestDispatcher()) + every { stringConst.APP_NAME } returns "Test" + + uartServer = UartServer(CoroutineScope(UnconfinedTestDispatcher())) + uartServer.start(spyk(), device) + } + } + + @Before + fun prepareLogger() { + mockkObject(NordicBlekLogger.Companion) + every { NordicBlekLogger.create(any(), any(), any(), any()) } returns mockk() + } + + @Test + fun `when connected should return state connected`() = runTest { + val connectedState = GattConnectionStateWithStatus( + GattConnectionState.STATE_CONNECTED, + BleGattConnectionStatus.SUCCESS + ) + viewModel.handleResult(NavigationResult.Success(device)) + + advanceUntilIdle() + + assertEquals(connectedState, viewModel.state.value.uartManagerState.connectionState) + } + + @Test + fun `when disconnected should return state connected`() = runTest { + val disconnectedState = GattConnectionStateWithStatus( + GattConnectionState.STATE_DISCONNECTED, + BleGattConnectionStatus.SUCCESS + ) + viewModel.handleResult(NavigationResult.Success(device)) + viewModel.onEvent(DisconnectEvent) + + advanceUntilIdle() + + assertEquals(disconnectedState, viewModel.state.value.uartManagerState.connectionState) + } +// +// @Test +// fun `when request last record then change status and get 1 record`() = runTest { +// viewModel.handleResult(NavigationResult.Success(device)) +// advanceUntilIdle() //Needed because of delay() in waitForBonding() +// assertEquals(RequestStatus.IDLE, viewModel.state.value.glsServiceData.requestStatus) +// +// viewModel.onEvent(OnWorkingModeSelected(WorkingMode.LAST)) +// assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) +// +// glsServer.continueWithResponse() //continue server breakpoint +// +// assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus) +// assertEquals(1, viewModel.state.value.glsServiceData.records.size) +// +// val parsedResponse = GlucoseMeasurementParser.parse(glsServer.OLDEST_RECORD) +// assertEquals(parsedResponse, viewModel.state.value.glsServiceData.records.keys.first()) +// } +// +// @Test +// fun `when request all record then change status and get 5 records`() = runTest { +// viewModel.handleResult(NavigationResult.Success(device)) +// advanceUntilIdle() //Needed because of delay() in waitForBonding() +// assertEquals(RequestStatus.IDLE, viewModel.state.value.glsServiceData.requestStatus) +// +// viewModel.onEvent(OnWorkingModeSelected(WorkingMode.ALL)) +// assertEquals(RequestStatus.PENDING, viewModel.state.value.glsServiceData.requestStatus) +// +// glsServer.continueWithResponse() //continue server breakpoint +// advanceUntilIdle() //We have to use because of delay() in sendAll() +// +// assertEquals(RequestStatus.SUCCESS, viewModel.state.value.glsServiceData.requestStatus) +// assertEquals(5, viewModel.state.value.glsServiceData.records.size) +// +// val expectedRecords = glsServer.records.map { GlucoseMeasurementParser.parse(it) } +// assertContentEquals(expectedRecords, viewModel.state.value.glsServiceData.records.keys) +// } +} From 4f769e90c2cc98f17863d6e3cf074a93218ca11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Fri, 28 Jul 2023 13:39:52 +0200 Subject: [PATCH 092/101] Sync changes --- app/build.gradle.kts | 4 +- build.gradle.kts | 3 +- gradle.properties | 2 +- gradle/libs.versions.toml | 238 ++++++++++++++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- lib_scanner/build.gradle.kts | 2 +- .../scanner/ExampleInstrumentedTest.kt | 22 -- .../toolbox/scanner/ScannerDestination.kt | 6 +- .../toolbox/scanner/ExampleUnitTest.kt | 16 -- lib_service/build.gradle.kts | 2 +- lib_ui/build.gradle.kts | 1 + .../android/ui/view/NordicLoggerFactory.kt | 5 +- .../ui/view/NordicLoggerFactoryHiltModule.kt | 8 +- lib_utils/build.gradle.kts | 2 +- profile_bps/build.gradle.kts | 2 +- .../nordicsemi/android/bps/view/BPSScreen.kt | 5 +- .../android/bps/viewmodel/BPSViewModel.kt | 30 +-- profile_cgms/build.gradle.kts | 2 +- .../android/cgms/repository/CGMRepository.kt | 8 +- .../android/cgms/repository/CGMService.kt | 43 ++-- .../nordicsemi/android/cgms/view/CGMScreen.kt | 5 +- profile_csc/build.gradle.kts | 2 +- .../android/csc/repository/CSCRepository.kt | 8 +- .../android/csc/repository/CSCService.kt | 23 +- .../nordicsemi/android/csc/view/CSCScreen.kt | 5 +- profile_gls/build.gradle.kts | 2 +- .../no/nordicsemi/android/gls/GlsServer.kt | 87 ++++--- .../android/gls/main/view/GLSScreen.kt | 6 +- .../gls/main/viewmodel/GLSViewModel.kt | 42 ++-- profile_hrs/build.gradle.kts | 2 +- .../android/hrs/service/HRSRepository.kt | 8 +- .../android/hrs/service/HRSService.kt | 22 +- .../nordicsemi/android/hrs/view/HRSScreen.kt | 5 +- profile_hts/build.gradle.kts | 2 +- .../android/hts/repository/HTSRepository.kt | 8 +- .../android/hts/repository/HTSService.kt | 22 +- .../nordicsemi/android/hts/view/HTSScreen.kt | 6 +- profile_prx/build.gradle.kts | 2 +- .../android/prx/repository/PRXRepository.kt | 8 +- .../android/prx/repository/PRXService.kt | 60 +++-- .../nordicsemi/android/prx/view/PRXScreen.kt | 5 +- profile_rscs/build.gradle.kts | 2 +- .../android/rscs/repository/RSCSRepository.kt | 8 +- .../android/rscs/repository/RSCSService.kt | 24 +- .../android/rscs/view/RSCSScreen.kt | 6 +- profile_uart/build.gradle.kts | 3 +- .../android/uart/repository/UARTRepository.kt | 4 +- .../android/uart/repository/UARTService.kt | 33 +-- .../android/uart/view/UARTScreen.kt | 6 +- .../gls/NordicLoggerFactoryTestModule.kt | 6 +- .../android/gls/UARTViewModelTest.kt | 20 +- renovate.json | 30 +++ settings.gradle.kts | 10 +- 53 files changed, 548 insertions(+), 337 deletions(-) create mode 100644 gradle/libs.versions.toml delete mode 100644 lib_scanner/src/androidTest/java/no/nordicsemi/android/toolbox/scanner/ExampleInstrumentedTest.kt delete mode 100644 lib_scanner/src/test/java/no/nordicsemi/android/toolbox/scanner/ExampleUnitTest.kt create mode 100644 renovate.json diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4959a6eb..4a305545 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -65,9 +65,9 @@ dependencies { implementation(libs.nordic.core) implementation(libs.nordic.theme) implementation(libs.nordic.navigation) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.uilogger) - implementation(libs.nordic.permission) + implementation(libs.nordic.permissions.ble) implementation(libs.nordic.analytics) implementation(libs.nordic.ble.common) diff --git a/build.gradle.kts b/build.gradle.kts index fa8c025b..b8e2230a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,7 +33,8 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.jvm) apply false - alias(libs.plugins.kotlin.kapt) apply true + alias(libs.plugins.kotlin.kapt) apply false + alias(libs.plugins.ksp) apply false alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.kotlin.parcelize) apply false alias(libs.plugins.hilt) apply false diff --git a/gradle.properties b/gradle.properties index e8268efb..cb91515f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -37,7 +37,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..2841082a --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,238 @@ +[versions] +accompanist = "0.30.1" +androidDesugarJdkLibs = "2.0.3" +androidGradlePlugin = "8.1.0" +androidxActivity = "1.7.2" +androidxAnnotation = "1.6.0" +androidxAppCompat = "1.6.1" +androidxComposeBom = "2023.06.01" # https://developer.android.com/jetpack/compose/bom/bom-mapping +androidxComposeCompiler = "1.5.1" # https://developer.android.com/jetpack/androidx/releases/compose-kotlin +androidxComposeRuntimeTracing = "1.0.0-alpha03" +androidxCore = "1.10.1" +androidxCoreSplashscreen = "1.0.1" +androidxDataStore = "1.0.0" +androidxEspresso = "3.5.1" +androidxHiltNavigationCompose = "1.0.0" +androidxLifecycle = "2.6.1" +androidxLocalbroadcastmanager = "1.1.0" +androidxMacroBenchmark = "1.1.1" +androidxNavigation = "2.6.0" +androidxMetrics = "1.0.0-alpha04" +androidxProfileinstaller = "1.3.1" +androidxStartup = "1.1.1" +androidxWindowManager = "1.1.0" +androidxTestCore = "1.5.0" +androidxTestExt = "1.1.5" +androidxTestRunner = "1.5.2" +androidxTestRules = "1.5.0" +androidxTracing = "1.1.0" +androidxUiAutomator = "2.2.0" +androidxWork = "2.8.1" +coil = "2.4.0" +firebaseBom = "32.2.0" +hilt = "2.47" +hiltExt = "1.0.0" +jacoco = "0.8.7" +junit4 = "4.13.2" +kotlin = "1.9.0" +kotlinxCoroutines = "1.7.3" +kotlinxDatetime = "0.4.0" +kotlinxSerializationJson = "1.5.1" +ksp = "1.9.0-1.0.12" +lint = "31.1.0" +memfault-cloud = "2.0.5" +okhttp = "4.11.0" +protobuf = "3.23.4" +protobufPlugin = "0.9.4" +publishPlugin = "1.2.0" +retrofit = "2.9.0" +retrofitKotlinxSerializationJson = "1.0.0" +room = "2.5.2" +secrets = "2.0.1" +turbine = "1.0.0" +markdown = "0.3.0" +wirePlugin = "4.7.2" +timber = "5.0.1" +chart = "v3.1.0" +leakcanary = "2.12" +mockk = "1.13.5" +slf4j = "2.0.7" +robolectric = "4.10.3" +skydovesBallon = "1.5.4" +moshiKotlin = "1.15.0" +moshiAdapters = "1.15.0" +okio = "3.4.0" + +nordic-blek = "0.2.1" +nordic-ble = "2.6.1" +nordic-dfu = "2.3.0" +nordic-log = "2.3.0" +nordic-mcumgr = "1.7.0" +nordic-scanner = "1.6.0" +nordic-common = "1.8.0" +nordic-memfault = "1.0.2" +nordicPlugins = "1.7.1" +dokkaPlugin = "1.8.20" +googleServicesPlugins = "4.3.15" +firebaseCrashlyticsPlugins = "2.9.2" + +[libraries] +accompanist-flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanist" } +accompanist-swiperefresh = { group = "com.google.accompanist", name = "accompanist-swiperefresh", version.ref = "accompanist" } +accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" } +accompanist-pager = { group = "com.google.accompanist", name = "accompanist-pager", version.ref = "accompanist" } +accompanist-pagerindicators = { group = "com.google.accompanist", name = "accompanist-pager-indicators", version.ref = "accompanist" } +accompanist-placeholder = { group = "com.google.accompanist", name = "accompanist-placeholder-material", version.ref = "accompanist" } +android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } +androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidxAnnotation" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } +androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } +androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } +androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } +androidx-compose-material = { group = "androidx.compose.material", name = "material" } +androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" } +androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } +androidx-compose-material3-windowSizeClass = {group = "androidx.compose.material3", name = "material3-window-size-class" } +androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" } +androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata" } +androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" } +androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } +androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util" } +androidx-core = { group = "androidx.core", name = "core", version.ref = "androidxCore" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" } +androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" } +androidx-dataStore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" } +androidx-dataStore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "androidxDataStore" } +androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } +androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidxLifecycle" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidxLifecycle" } +androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime", version.ref = "androidxLifecycle" } +androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } +androidx-lifecycle-service = { group = "androidx.lifecycle", name = "lifecycle-service", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewModel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewModel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewModel-savedState = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-savedstate", version.ref = "androidxLifecycle" } +androidx-localbroadcastmanager = { group = "androidx.localbroadcastmanager", name = "localbroadcastmanager", version.ref = "androidxLocalbroadcastmanager" } +androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", version.ref = "androidxMetrics" } +androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" } +androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "androidxNavigation" } +androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "androidxProfileinstaller" } +androidx-startup = { group = "androidx.startup", name = "startup-runtime", version.ref = "androidxStartup" } +androidx-window-manager = {module = "androidx.window:window", version.ref = "androidxWindowManager"} +androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" } +androidx-test-ext = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxTestExt" } +androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxEspresso" } +androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTestRunner" } +androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTestRules" } +androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "androidxUiAutomator" } +androidx-tracing-ktx = {group = "androidx.tracing", name="tracing-ktx", version.ref = "androidxTracing" } +androidx-work-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "androidxWork" } +androidx-work-testing = { group = "androidx.work", name = "work-testing", version.ref = "androidxWork" } +coil-kt = { group = "io.coil-kt", name = "coil", version.ref = "coil"} +coil-kt-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil"} +coil-kt-svg = { group = "io.coil-kt", name = "coil-svg", version.ref = "coil"} +firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom"} +firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics-ktx" } +firebase-database = { group = "com.google.firebase", name = "firebase-database-ktx" } +firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" } +hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } +hilt-ext-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hiltExt" } +hilt-ext-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hiltExt" } +hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } +hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" } +junit4 = { group = "junit", name = "junit", version.ref = "junit4" } +kotlin-junit = { group = "org.jetbrains.kotlin", name = "kotlin-test-junit", version.ref = "kotlin" } +kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" } +kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } +kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } +kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } +lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "lint" } +okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } +protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } +protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } +turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" } +retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } +retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofitKotlinxSerializationJson" } +room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } +room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } +room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } +markdown = { group = "com.github.jeziellago", name = "compose-markdown", version.ref = "markdown" } +memfault-cloud = { group = "com.memfault.cloud", name = "cloud-android", version.ref = "memfault-cloud" } +timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" } +chart = { group = "com.github.PhilJay", name = "MPAndroidChart", version.ref = "chart" } +leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary" } +test-slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" } +test-mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" } +test-robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } +skydoves-ballon = {group = "com.github.skydoves", name = "balloon-compose", version.ref = "skydovesBallon"} +moshi-kotlin = {group = "com.squareup.moshi", name = "moshi-kotlin", version.ref = "moshiKotlin"} +moshi-adapters = {group = "com.squareup.moshi", name = "moshi-adapters", version.ref = "moshiAdapters"} +okio = {group = "com.squareup.okio", name = "okio", version.ref = "okio"} + +# Nordic +nordic-ble-ktx = { group = "no.nordicsemi.android", name = "ble-ktx", version.ref = "nordic-ble" } +nordic-ble-common = { group = "no.nordicsemi.android", name = "ble-common", version.ref = "nordic-ble" } +nordic-dfu = { group = "no.nordicsemi.android", name = "dfu", version.ref = "nordic-dfu" } +nordic-mcumgr-core = { group = "no.nordicsemi.android", name = "mcumgr-core", version.ref = "nordic-mcumgr" } +nordic-mcumgr-ble = { group = "no.nordicsemi.android", name = "mcumgr-ble", version.ref = "nordic-mcumgr" } +nordic-log = { group = "no.nordicsemi.android", name = "log", version.ref = "nordic-log" } +nordic-log-timber = { group = "no.nordicsemi.android", name = "log-timber", version.ref = "nordic-log" } +nordic-scanner = { group = "no.nordicsemi.android.support.v18", name = "scanner", version.ref = "nordic-scanner" } +nordic-core = { group = "no.nordicsemi.android.common", name = "core", version.ref = "nordic-common" } +nordic-theme = { group = "no.nordicsemi.android.common", name = "theme", version.ref = "nordic-common" } +nordic-analytics = { group = "no.nordicsemi.android.common", name = "analytics", version.ref = "nordic-common" } +nordic-navigation = { group = "no.nordicsemi.android.common", name = "navigation", version.ref = "nordic-common" } +nordic-uilogger = { group = "no.nordicsemi.android.common", name = "uilogger", version.ref = "nordic-common" } +nordic-logger = { group = "no.nordicsemi.android.common", name = "logger", version.ref = "nordic-common" } +nordic-permissions-nfc = { group = "no.nordicsemi.android.common", name = "permissions-nfc", version.ref = "nordic-common" } +nordic-permissions-ble = { group = "no.nordicsemi.android.common", name = "permissions-ble", version.ref = "nordic-common" } +nordic-permissions-internet = { group = "no.nordicsemi.android.common", name = "permissions-internet", version.ref = "nordic-common" } +nordic-memfault = { group = "no.nordicsemi.android", name = "memfault", version.ref = "nordic-memfault" } + +# BleK +nordic-blek-client = { group = "no.nordicsemi.android.kotlin.ble", name = "client", version.ref = "nordic-blek" } +nordic-blek-server = { group = "no.nordicsemi.android.kotlin.ble", name = "server", version.ref = "nordic-blek" } +nordic-blek-profile = { group = "no.nordicsemi.android.kotlin.ble", name = "profile", version.ref = "nordic-blek" } +nordic-blek-core = { group = "no.nordicsemi.android.kotlin.ble", name = "core", version.ref = "nordic-blek" } +nordic-blek-scanner = { group = "no.nordicsemi.android.kotlin.ble", name = "scanner", version.ref = "nordic-blek" } +nordic-blek-advertiser = { group = "no.nordicsemi.android.kotlin.ble", name = "advertiser", version.ref = "nordic-blek" } +nordic-blek-uiscanner = { group = "no.nordicsemi.android.kotlin.ble", name = "uiscanner", version.ref = "nordic-blek" } + +# Dependencies of the included build-logic +android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } +kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } + +[plugins] +nordic-application = { id = "no.nordicsemi.android.gradle.application", version.ref = "nordicPlugins" } +nordic-application-compose = { id = "no.nordicsemi.android.gradle.application.compose", version.ref = "nordicPlugins" } +nordic-library = { id = "no.nordicsemi.android.gradle.library", version.ref = "nordicPlugins" } +nordic-library-compose = { id = "no.nordicsemi.android.gradle.library.compose", version.ref = "nordicPlugins" } +nordic-feature = { id = "no.nordicsemi.android.gradle.feature", version.ref = "nordicPlugins" } +nordic-hilt = { id = "no.nordicsemi.android.gradle.hilt", version.ref = "nordicPlugins" } +nordic-kotlin = { id = "no.nordicsemi.android.gradle.kotlin", version.ref = "nordicPlugins" } +nordic-nexus = { id = "no.nordicsemi.android.gradle.nexus", version.ref = "nordicPlugins" } +android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } +android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } +android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } +kotlin-dokka = { id = "org.jetbrains.dokka", version.ref = "dokkaPlugin" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } +protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } +secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } +publish = { id = "com.gradle.plugin-publish", version.ref = "publishPlugin" } +wire = { id = "com.squareup.wire", version.ref = "wirePlugin" } +google-services = { id = "com.google.gms.google-services", version.ref = "googleServicesPlugins" } +firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugins" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d749aba9..dfd5018b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -31,7 +31,7 @@ #Mon Feb 14 14:46:55 CET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/lib_scanner/build.gradle.kts b/lib_scanner/build.gradle.kts index 90a4d011..51a09721 100644 --- a/lib_scanner/build.gradle.kts +++ b/lib_scanner/build.gradle.kts @@ -39,7 +39,7 @@ android { dependencies { implementation(libs.nordic.navigation) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.blek.scanner) diff --git a/lib_scanner/src/androidTest/java/no/nordicsemi/android/toolbox/scanner/ExampleInstrumentedTest.kt b/lib_scanner/src/androidTest/java/no/nordicsemi/android/toolbox/scanner/ExampleInstrumentedTest.kt deleted file mode 100644 index 9f3317ee..00000000 --- a/lib_scanner/src/androidTest/java/no/nordicsemi/android/toolbox/scanner/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package no.nordicsemi.android.toolbox.scanner - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.* -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("no.nordicsemi.android.toolbox.scanner.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt index 2373fc08..9c44d07c 100644 --- a/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt +++ b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt @@ -5,10 +5,10 @@ import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.navigation.createDestination import no.nordicsemi.android.common.navigation.defineDestination import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewModel -import no.nordicsemi.android.common.ui.scanner.DeviceSelected -import no.nordicsemi.android.common.ui.scanner.ScannerScreen -import no.nordicsemi.android.common.ui.scanner.ScanningCancelled import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.ui.scanner.DeviceSelected +import no.nordicsemi.android.kotlin.ble.ui.scanner.ScannerScreen +import no.nordicsemi.android.kotlin.ble.ui.scanner.ScanningCancelled val ScannerDestinationId = createDestination("uiscanner-destination") diff --git a/lib_scanner/src/test/java/no/nordicsemi/android/toolbox/scanner/ExampleUnitTest.kt b/lib_scanner/src/test/java/no/nordicsemi/android/toolbox/scanner/ExampleUnitTest.kt deleted file mode 100644 index f3df530f..00000000 --- a/lib_scanner/src/test/java/no/nordicsemi/android/toolbox/scanner/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package no.nordicsemi.android.toolbox.scanner - -import org.junit.Assert.* -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/lib_service/build.gradle.kts b/lib_service/build.gradle.kts index b0e295c3..4227f919 100644 --- a/lib_service/build.gradle.kts +++ b/lib_service/build.gradle.kts @@ -42,7 +42,7 @@ dependencies { implementation(project(":lib_ui")) implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.blek.core) diff --git a/lib_ui/build.gradle.kts b/lib_ui/build.gradle.kts index e8a0ff7a..3d577f0f 100644 --- a/lib_ui/build.gradle.kts +++ b/lib_ui/build.gradle.kts @@ -51,4 +51,5 @@ dependencies { implementation(libs.androidx.compose.material3) implementation(libs.androidx.activity.compose) implementation(libs.nordic.blek.client) + implementation(libs.nordic.logger) } diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt index 3ffbda8a..3e668674 100644 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt @@ -1,10 +1,9 @@ package no.nordicsemi.android.ui.view import android.content.Context -import no.nordicsemi.android.common.logger.BlekLogger -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher interface NordicLoggerFactory { - fun createNordicLogger(context: Context, profile: String?, key: String, name: String?): BlekLoggerAndLauncher + fun createNordicLogger(context: Context, profile: String?, key: String, name: String?): BleLoggerAndLauncher } diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt index 8550b29d..ce8bc4a7 100644 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt @@ -5,8 +5,8 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import no.nordicsemi.android.common.logger.NordicBlekLogger -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.DefaultBleLogger @Module @InstallIn(SingletonComponent::class) @@ -20,8 +20,8 @@ class NordicLoggerFactoryHiltModule { profile: String?, key: String, name: String?, - ): BlekLoggerAndLauncher { - return NordicBlekLogger.create(context, profile, key, name) + ): BleLoggerAndLauncher { + return DefaultBleLogger.create(context, profile, key, name) } } } diff --git a/lib_utils/build.gradle.kts b/lib_utils/build.gradle.kts index 9657da5e..4be89537 100644 --- a/lib_utils/build.gradle.kts +++ b/lib_utils/build.gradle.kts @@ -40,6 +40,6 @@ android { dependencies { implementation(libs.nordic.navigation) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.kotlinx.coroutines.core) } diff --git a/profile_bps/build.gradle.kts b/profile_bps/build.gradle.kts index 06243d7e..5a8f75da 100644 --- a/profile_bps/build.gradle.kts +++ b/profile_bps/build.gradle.kts @@ -54,7 +54,7 @@ dependencies { implementation(libs.nordic.theme) implementation(libs.nordic.uilogger) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.hilt.navigation.compose) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index da253bdb..60a469f9 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -44,13 +44,12 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.bps.R import no.nordicsemi.android.bps.viewmodel.BPSViewModel -import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView -import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar -@OptIn(ExperimentalMaterial3Api::class) @Composable fun BPSScreen() { val viewModel: BPSViewModel = hiltViewModel() diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index ca44f900..5e51edcd 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -53,13 +53,12 @@ import no.nordicsemi.android.bps.view.BPSViewEvent import no.nordicsemi.android.bps.view.BPSViewState import no.nordicsemi.android.bps.view.DisconnectEvent import no.nordicsemi.android.bps.view.OpenLoggerEvent -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher -import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.DefaultBleLogger import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator -import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.client.main.connect -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -93,8 +92,8 @@ internal class BPSViewModel @Inject constructor( private val _state = MutableStateFlow(BPSViewState()) val state = _state.asStateFlow() - private lateinit var client: BleGattClient - private lateinit var logger: BlekLoggerAndLauncher + private lateinit var client: ClientBleGatt + private lateinit var logger: BleLoggerAndLauncher init { navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(BPS_SERVICE_UUID)) @@ -126,9 +125,9 @@ internal class BPSViewModel @Inject constructor( private fun startGattClient(device: ServerDevice) = viewModelScope.launch { _state.value = _state.value.copy(deviceName = device.name) - logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "BPS", device.address) + logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "BPS", device.address) - client = device.connect(context, logger = logger) + client = ClientBleGatt.connect(context, device, logger = logger) client.connectionStateWithStatus .filterNotNull() @@ -140,14 +139,15 @@ internal class BPSViewModel @Inject constructor( return@launch } - client.discoverServices() - .filterNotNull() - .onEach { configureGatt(it) } - .catch { onMissingServices() } - .launchIn(viewModelScope) + try { + val services = client.discoverServices() + configureGatt(services) + } catch (e: Exception) { + onMissingServices() + } } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: ClientBleGattServices) { val bpsService = services.findService(BPS_SERVICE_UUID)!! val bpmCharacteristic = bpsService.findCharacteristic(BPM_CHARACTERISTIC_UUID)!! val icpCharacteristic = bpsService.findCharacteristic(ICP_CHARACTERISTIC_UUID) diff --git a/profile_cgms/build.gradle.kts b/profile_cgms/build.gradle.kts index fd846bfe..b7f82f0e 100644 --- a/profile_cgms/build.gradle.kts +++ b/profile_cgms/build.gradle.kts @@ -53,7 +53,7 @@ dependencies { implementation(libs.nordic.uilogger) implementation(libs.nordic.theme) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.core) 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 c9dc5d1d..7ef38ebf 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 @@ -41,8 +41,8 @@ import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber import no.nordicsemi.android.cgms.data.CGMServiceCommand import no.nordicsemi.android.cgms.data.CGMServiceData import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher -import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.DefaultBleLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -60,7 +60,7 @@ class CGMRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: BlekLoggerAndLauncher? = null + private var logger: BleLoggerAndLauncher? = null private val _data = MutableStateFlow(CGMServiceData()) internal val data = _data.asStateFlow() @@ -93,7 +93,7 @@ class CGMRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "CGM", device.address) + logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "CGM", device.address) _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 b6e6975c..5cb4fac5 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 @@ -41,15 +41,12 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.ble.common.data.cgm.CGMSpecificOpsControlPointData import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber import no.nordicsemi.android.cgms.data.CGMServiceCommand -import no.nordicsemi.android.common.logger.NordicBlekLogger -import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt import no.nordicsemi.android.kotlin.ble.client.main.errors.GattOperationException -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -60,6 +57,8 @@ import no.nordicsemi.android.kotlin.ble.profile.cgm.CGMSpecificOpsControlPointPa import no.nordicsemi.android.kotlin.ble.profile.cgm.CGMStatusParser import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMErrorCode import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMOpCode +import no.nordicsemi.android.kotlin.ble.profile.cgm.data.CGMSpecificOpsControlPointData +import no.nordicsemi.android.kotlin.ble.profile.gls.CGMSpecificOpsControlPointDataParser import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointParser import no.nordicsemi.android.kotlin.ble.profile.gls.data.NumberOfRecordsData @@ -70,7 +69,6 @@ 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.ui.view.StringConst import no.nordicsemi.android.utils.launchWithCatch import java.util.* import javax.inject.Inject @@ -93,7 +91,7 @@ internal class CGMService : NotificationService() { @Inject lateinit var repository: CGMRepository - private lateinit var client: BleGattClient + private lateinit var client: ClientBleGatt private var secured = false @@ -101,7 +99,7 @@ internal class CGMService : NotificationService() { private var sessionStartTime: Long = 0 - private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic + private lateinit var recordAccessControlPointCharacteristic: ClientBleGattCharacteristic override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -133,7 +131,7 @@ internal class CGMService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@CGMService, logger = { p, s -> repository.log(p, s) }) + client = ClientBleGatt.connect(this@CGMService, device, logger = { p, s -> repository.log(p, s) }) client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } @@ -145,14 +143,15 @@ internal class CGMService : NotificationService() { return@launch } - client.discoverServices() - .filterNotNull() - .onEach { configureGatt(it) } - .catch { repository.onMissingServices() } - .launchIn(lifecycleScope) + try { + val services = client.discoverServices() + configureGatt(services) + } catch (e: Exception) { + repository.onMissingServices() + } } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: ClientBleGattServices) { val cgmService = services.findService(CGMS_SERVICE_UUID)!! val statusCharacteristic = cgmService.findCharacteristic(CGM_STATUS_UUID)!! val featureCharacteristic = cgmService.findCharacteristic(CGM_FEATURE_UUID)!! @@ -225,7 +224,7 @@ internal class CGMService : NotificationService() { } if (sessionStartTime == 0L) { - opsControlPointCharacteristic.write(CGMSpecificOpsControlPointData.startSession(secured).value!!) + opsControlPointCharacteristic.write(CGMSpecificOpsControlPointDataParser.startSession(secured)) } } @@ -262,11 +261,11 @@ internal class CGMService : NotificationService() { if (numberOfRecords > 0) { if (repository.hasRecords) { recordAccessControlPointCharacteristic.write( - RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(repository.highestSequenceNumber).value + RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(repository.highestSequenceNumber) ) } else { recordAccessControlPointCharacteristic.write( - RecordAccessControlPointInputParser.reportAllStoredRecords().value + RecordAccessControlPointInputParser.reportAllStoredRecords() ) } } @@ -289,7 +288,7 @@ internal class CGMService : NotificationService() { clear() repository.onNewRequestStatus(RequestStatus.PENDING) try { - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord()) } catch (e: GattOperationException) { e.printStackTrace() repository.onNewRequestStatus(RequestStatus.FAILED) @@ -300,7 +299,7 @@ internal class CGMService : NotificationService() { clear() repository.onNewRequestStatus(RequestStatus.PENDING) try { - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord()) } catch (e: GattOperationException) { e.printStackTrace() repository.onNewRequestStatus(RequestStatus.FAILED) @@ -311,7 +310,7 @@ internal class CGMService : NotificationService() { clear() repository.onNewRequestStatus(RequestStatus.PENDING) try { - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords()) } catch (e: GattOperationException) { e.printStackTrace() repository.onNewRequestStatus(RequestStatus.FAILED) 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 8cbd1b8d..23d2dd45 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,13 +44,12 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.cgms.R 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 import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar -@OptIn(ExperimentalMaterial3Api::class) @Composable fun CGMScreen() { val viewModel: CGMViewModel = hiltViewModel() diff --git a/profile_csc/build.gradle.kts b/profile_csc/build.gradle.kts index 1fa6038d..1d66f68c 100644 --- a/profile_csc/build.gradle.kts +++ b/profile_csc/build.gradle.kts @@ -54,7 +54,7 @@ dependencies { implementation(libs.nordic.theme) implementation(libs.nordic.navigation) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.core) implementation(libs.androidx.hilt.navigation.compose) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 92510765..2964f664 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -38,8 +38,8 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher -import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.DefaultBleLogger import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -61,7 +61,7 @@ class CSCRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: BlekLoggerAndLauncher? = null + private var logger: BleLoggerAndLauncher? = null private val _wheelSize = MutableStateFlow(WheelSizes.default) internal val wheelSize = _wheelSize.asStateFlow() @@ -92,7 +92,7 @@ class CSCRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "CSC", device.address) + logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "CSC", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(CSCService::class.java, device) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 50143899..aa750bf5 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -41,9 +41,8 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.client.main.connect -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -51,7 +50,6 @@ import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.csc.CSCDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.android.ui.view.NordicLoggerFactory import java.util.* import javax.inject.Inject @@ -68,7 +66,7 @@ internal class CSCService : NotificationService() { @Inject lateinit var repository: CSCRepository - private lateinit var client: BleGattClient + private lateinit var client: ClientBleGatt override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -87,7 +85,7 @@ internal class CSCService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@CSCService, logger = { p, s -> repository.log(p, s) }) + client = ClientBleGatt.connect(this@CSCService, device, logger = { p, s -> repository.log(p, s) }) client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } @@ -99,14 +97,15 @@ internal class CSCService : NotificationService() { return@launch } - client.discoverServices() - .filterNotNull() - .onEach { configureGatt(it) } - .catch { repository.onMissingServices() } - .launchIn(lifecycleScope) + try { + val services = client.discoverServices() + configureGatt(services) + } catch (e: Exception) { + repository.onMissingServices() + } } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: ClientBleGattServices) { val cscService = services.findService(CSC_SERVICE_UUID)!! val cscMeasurementCharacteristic = cscService.findCharacteristic(CSC_MEASUREMENT_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt index 7b6a9a9a..6accb0f6 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCScreen.kt @@ -42,15 +42,14 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView -import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.viewmodel.CSCViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar -@OptIn(ExperimentalMaterial3Api::class) @Composable fun CSCScreen() { val viewModel: CSCViewModel = hiltViewModel() diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index 142d404d..8c7f71e8 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -55,7 +55,7 @@ dependencies { implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) implementation(libs.nordic.theme) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) diff --git a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt index 53128aa5..c1d5490c 100644 --- a/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt +++ b/profile_gls/src/debug/java/no/nordicsemi/android/gls/GlsServer.kt @@ -4,11 +4,11 @@ import android.annotation.SuppressLint import android.content.Context import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.gls.main.viewmodel.BATTERY_LEVEL_CHARACTERISTIC_UUID import no.nordicsemi.android.gls.main.viewmodel.BATTERY_SERVICE_UUID import no.nordicsemi.android.gls.main.viewmodel.GLS_SERVICE_UUID @@ -16,17 +16,17 @@ import no.nordicsemi.android.gls.main.viewmodel.GLUCOSE_MEASUREMENT_CHARACTERIST import no.nordicsemi.android.gls.main.viewmodel.GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC import no.nordicsemi.android.gls.main.viewmodel.RACP_CHARACTERISTIC import no.nordicsemi.android.kotlin.ble.advertiser.BleAdvertiser -import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertiseConfig import no.nordicsemi.android.kotlin.ble.core.MockServerDevice +import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertisingConfig import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser -import no.nordicsemi.android.kotlin.ble.server.main.BleGattServer -import no.nordicsemi.android.kotlin.ble.server.main.service.BleGattServerServiceType -import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristic -import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristicConfig -import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattServiceConfig -import no.nordicsemi.android.kotlin.ble.server.main.service.BluetoothGattServerConnection +import no.nordicsemi.android.kotlin.ble.server.main.ServerBleGatt +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattCharacteristicConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattServiceConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattServiceType +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBluetoothGattConnection import javax.inject.Inject import javax.inject.Singleton @@ -38,27 +38,27 @@ class GlsServer @Inject constructor( private val scope: CoroutineScope ) { - lateinit var server: BleGattServer + lateinit var server: ServerBleGatt - lateinit var glsCharacteristic: BleServerGattCharacteristic - lateinit var glsContextCharacteristic: BleServerGattCharacteristic - lateinit var racpCharacteristic: BleServerGattCharacteristic - lateinit var batteryLevelCharacteristic: BleServerGattCharacteristic + lateinit var glsCharacteristic: ServerBleGattCharacteristic + lateinit var glsContextCharacteristic: ServerBleGattCharacteristic + lateinit var racpCharacteristic: ServerBleGattCharacteristic + lateinit var batteryLevelCharacteristic: ServerBleGattCharacteristic - private var lastRequest = byteArrayOf() + private var lastRequest = DataByteArray() - val YOUNGEST_RECORD = byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11) - val OLDEST_RECORD = byteArrayOf(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11) + val YOUNGEST_RECORD = DataByteArray.from(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11) + val OLDEST_RECORD = DataByteArray.from(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11) val records = listOf( YOUNGEST_RECORD, - byteArrayOf(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11), - byteArrayOf(0x07, 0x02, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0B, 0x00, 0x00, 0x54, 0xD2.toByte(), 0x11), - byteArrayOf(0x07, 0x03, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0E, 0x00, 0x00, 0x6B, 0xD2.toByte(), 0x11), + DataByteArray.from(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11), + DataByteArray.from(0x07, 0x02, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0B, 0x00, 0x00, 0x54, 0xD2.toByte(), 0x11), + DataByteArray.from(0x07, 0x03, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0E, 0x00, 0x00, 0x6B, 0xD2.toByte(), 0x11), OLDEST_RECORD ) - val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01) + val racp = DataByteArray.from(0x06, 0x00, 0x01, 0x01) fun start( context: Context, @@ -67,50 +67,50 @@ class GlsServer @Inject constructor( address = "55:44:33:22:11" ), ) = scope.launch { - val gmCharacteristic = BleServerGattCharacteristicConfig( + val gmCharacteristic = ServerBleGattCharacteristicConfig( GLUCOSE_MEASUREMENT_CHARACTERISTIC, listOf(BleGattProperty.PROPERTY_NOTIFY), listOf() ) - val gmContextCharacteristic = BleServerGattCharacteristicConfig( + val gmContextCharacteristic = ServerBleGattCharacteristicConfig( GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC, listOf(BleGattProperty.PROPERTY_NOTIFY), listOf() ) - val racpCharacteristic = BleServerGattCharacteristicConfig( + val racpCharacteristic = ServerBleGattCharacteristicConfig( RACP_CHARACTERISTIC, listOf(BleGattProperty.PROPERTY_INDICATE, BleGattProperty.PROPERTY_WRITE), listOf(BleGattPermission.PERMISSION_WRITE) ) - val serviceConfig = BleServerGattServiceConfig( + val serviceConfig = ServerBleGattServiceConfig( GLS_SERVICE_UUID, - BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + ServerBleGattServiceType.SERVICE_TYPE_PRIMARY, listOf(gmCharacteristic, gmContextCharacteristic, racpCharacteristic) ) - val batteryLevelCharacteristic = BleServerGattCharacteristicConfig( + val batteryLevelCharacteristic = ServerBleGattCharacteristicConfig( BATTERY_LEVEL_CHARACTERISTIC_UUID, listOf(BleGattProperty.PROPERTY_READ, BleGattProperty.PROPERTY_NOTIFY), listOf(BleGattPermission.PERMISSION_READ) ) - val batteryService = BleServerGattServiceConfig( + val batteryService = ServerBleGattServiceConfig( BATTERY_SERVICE_UUID, - BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + ServerBleGattServiceType.SERVICE_TYPE_PRIMARY, listOf(batteryLevelCharacteristic) ) - server = BleGattServer.create( + server = ServerBleGatt.create( context = context, config = arrayOf(serviceConfig, batteryService), mock = device ) val advertiser = BleAdvertiser.create(context) - advertiser.advertise(config = BleAdvertiseConfig(), mock = device).launchIn(scope) + advertiser.advertise(config = BleAdvertisingConfig(), mock = device).launchIn(scope) launch { server.connections @@ -123,7 +123,7 @@ class GlsServer @Inject constructor( server.stopServer() } - private fun setUpConnection(connection: BluetoothGattServerConnection) { + private fun setUpConnection(connection: ServerBluetoothGattConnection) { val glsService = connection.services.findService(GLS_SERVICE_UUID)!! glsCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!! glsContextCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC)!! @@ -137,9 +137,8 @@ class GlsServer @Inject constructor( // startBatteryService(connection) } - private fun startGlsService(connection: BluetoothGattServerConnection) { + private fun startGlsService(connection: ServerBluetoothGattConnection) { racpCharacteristic.value - .filter { it.isNotEmpty() } .onEach { lastRequest = it } .launchIn(scope) } @@ -148,42 +147,42 @@ class GlsServer @Inject constructor( sendResponse(lastRequest) } - private fun sendResponse(request: ByteArray) { - if (request.contentEquals(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value)) { + private fun sendResponse(request: DataByteArray) { + if (request == RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords()) { sendAll(glsCharacteristic) racpCharacteristic.setValue(racp) - } else if (request.contentEquals(RecordAccessControlPointInputParser.reportLastStoredRecord().value)) { + } else if (request == RecordAccessControlPointInputParser.reportLastStoredRecord()) { sendLast(glsCharacteristic) racpCharacteristic.setValue(racp) - } else if (request.contentEquals(RecordAccessControlPointInputParser.reportFirstStoredRecord().value)) { + } else if (request == RecordAccessControlPointInputParser.reportFirstStoredRecord()) { sendFirst(glsCharacteristic) racpCharacteristic.setValue(racp) } } - private fun sendFirst(characteristics: BleServerGattCharacteristic) { + private fun sendFirst(characteristics: ServerBleGattCharacteristic) { characteristics.setValue(records.first()) } - private fun sendLast(characteristics: BleServerGattCharacteristic) { + private fun sendLast(characteristics: ServerBleGattCharacteristic) { characteristics.setValue(records.last()) } - private fun sendAll(characteristics: BleServerGattCharacteristic) = scope.launch { + private fun sendAll(characteristics: ServerBleGattCharacteristic) = scope.launch { records.forEach { characteristics.setValue(it) delay(100) } } - private fun startBatteryService(connection: BluetoothGattServerConnection) { + private fun startBatteryService(connection: ServerBluetoothGattConnection) { scope.launch { repeat(100) { - batteryLevelCharacteristic.setValue(byteArrayOf(0x61)) + batteryLevelCharacteristic.setValue(DataByteArray.from(0x61)) delay(STANDARD_DELAY) - batteryLevelCharacteristic.setValue(byteArrayOf(0x60)) + batteryLevelCharacteristic.setValue(DataByteArray.from(0x60)) delay(STANDARD_DELAY) - batteryLevelCharacteristic.setValue(byteArrayOf(0x5F)) + batteryLevelCharacteristic.setValue(DataByteArray.from(0x5F)) delay(STANDARD_DELAY) } } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt index a47ebb93..f7df8e4b 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/view/GLSScreen.kt @@ -31,7 +31,6 @@ package no.nordicsemi.android.gls.main.view -import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -43,15 +42,14 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView -import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.gls.R import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar -@OptIn(ExperimentalMaterial3Api::class) @Composable fun GLSScreen() { val viewModel: GLSViewModel = hiltViewModel() diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index 09a3352c..1057714b 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -49,7 +49,7 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.GlsDetailsDestinationId @@ -60,11 +60,10 @@ import no.nordicsemi.android.gls.main.view.GLSViewState import no.nordicsemi.android.gls.main.view.OnGLSRecordClick import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected import no.nordicsemi.android.gls.main.view.OpenLoggerEvent -import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.client.main.connect +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt import no.nordicsemi.android.kotlin.ble.client.main.errors.GattOperationException -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -107,11 +106,11 @@ internal class GLSViewModel @Inject constructor( private val loggerFactory: NordicLoggerFactory ) : ViewModel() { - internal lateinit var client: BleGattClient - private lateinit var logger: BlekLoggerAndLauncher + internal lateinit var client: ClientBleGatt + private lateinit var logger: BleLoggerAndLauncher - private lateinit var glucoseMeasurementCharacteristic: BleGattCharacteristic - private lateinit var recordAccessControlPointCharacteristic: BleGattCharacteristic + private lateinit var glucoseMeasurementCharacteristic: ClientBleGattCharacteristic + private lateinit var recordAccessControlPointCharacteristic: ClientBleGattCharacteristic private val _state = MutableStateFlow(GLSViewState()) val state = _state.asStateFlow() @@ -170,7 +169,7 @@ internal class GLSViewModel @Inject constructor( logger = loggerFactory.createNordicLogger(context, stringConst.APP_NAME, "GLS", device.address) - client = device.connect(context, logger = logger) + client = ClientBleGatt.connect(context, device, logger = logger) client.waitForBonding() @@ -184,11 +183,12 @@ internal class GLSViewModel @Inject constructor( return@launch } - client.discoverServices() - .filterNotNull() - .onEach { configureGatt(it) } - .catch { onMissingServices() } - .launchIn(viewModelScope) + try { + val services = client.discoverServices() + configureGatt(services) + } catch (e: Exception) { + onMissingServices() + } } private fun onMissingServices() { @@ -202,7 +202,7 @@ internal class GLSViewModel @Inject constructor( } } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: ClientBleGattServices) { val glsService = services.findService(GLS_SERVICE_UUID)!! glucoseMeasurementCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!! recordAccessControlPointCharacteristic = glsService.findCharacteristic(RACP_CHARACTERISTIC)!! @@ -268,11 +268,11 @@ internal class GLSViewModel @Inject constructor( try { if (state.value.glsServiceData.records.isNotEmpty()) { recordAccessControlPointCharacteristic.write( - RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(highestSequenceNumber).value + RecordAccessControlPointInputParser.reportStoredRecordsGreaterThenOrEqualTo(highestSequenceNumber) ) } else { recordAccessControlPointCharacteristic.write( - RecordAccessControlPointInputParser.reportAllStoredRecords().value + RecordAccessControlPointInputParser.reportAllStoredRecords() ) } } catch (e: GattOperationException) { @@ -298,7 +298,7 @@ internal class GLSViewModel @Inject constructor( clear() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) try { - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord().value) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportLastStoredRecord()) } catch (e: Exception) { e.printStackTrace() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) @@ -309,7 +309,7 @@ internal class GLSViewModel @Inject constructor( clear() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) try { - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord().value) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportFirstStoredRecord()) } catch (e: Exception) { e.printStackTrace() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) @@ -320,7 +320,7 @@ internal class GLSViewModel @Inject constructor( clear() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.PENDING) try { - recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value) + recordAccessControlPointCharacteristic.write(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords()) } catch (e: Exception) { e.printStackTrace() _state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED) diff --git a/profile_hrs/build.gradle.kts b/profile_hrs/build.gradle.kts index 289a8241..e03228fc 100644 --- a/profile_hrs/build.gradle.kts +++ b/profile_hrs/build.gradle.kts @@ -54,7 +54,7 @@ dependencies { implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) implementation(libs.nordic.navigation) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.uilogger) implementation(libs.nordic.core) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 29db9d17..220bd843 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -38,8 +38,8 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher -import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.DefaultBleLogger import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState @@ -58,7 +58,7 @@ class HRSRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: BlekLoggerAndLauncher? = null + private var logger: BleLoggerAndLauncher? = null private val _data = MutableStateFlow(HRSServiceData()) internal val data = _data.asStateFlow() @@ -86,7 +86,7 @@ class HRSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "HRS", device.address) + logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "HRS", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(HRSService::class.java, device) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index b55b4da5..af1ea88c 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -41,9 +41,8 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.client.main.connect -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -69,7 +68,7 @@ internal class HRSService : NotificationService() { @Inject lateinit var repository: HRSRepository - private lateinit var client: BleGattClient + private lateinit var client: ClientBleGatt override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -88,7 +87,7 @@ internal class HRSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@HRSService, logger = { p, s -> repository.log(p, s) }) + client = ClientBleGatt.connect(this@HRSService, device, logger = { p, s -> repository.log(p, s) }) client.waitForBonding() @@ -102,14 +101,15 @@ internal class HRSService : NotificationService() { return@launch } - client.discoverServices() - .filterNotNull() - .onEach { configureGatt(it) } - .catch { repository.onMissingServices() } - .launchIn(lifecycleScope) + try { + val services = client.discoverServices() + configureGatt(services) + } catch (e: Exception) { + repository.onMissingServices() + } } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: ClientBleGattServices) { val hrsService = services.findService(HRS_SERVICE_UUID)!! val hrsMeasurementCharacteristic = hrsService.findCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID)!! val bodySensorLocationCharacteristic = hrsService.findCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID)!! diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index 70b67d4a..991aed56 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -42,15 +42,14 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView -import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.hrs.R import no.nordicsemi.android.hrs.viewmodel.HRSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar -@OptIn(ExperimentalMaterial3Api::class) @Composable fun HRSScreen() { val viewModel: HRSViewModel = hiltViewModel() diff --git a/profile_hts/build.gradle.kts b/profile_hts/build.gradle.kts index 8edf97f1..8355510b 100644 --- a/profile_hts/build.gradle.kts +++ b/profile_hts/build.gradle.kts @@ -52,7 +52,7 @@ dependencies { implementation(libs.nordic.ble.ktx) implementation(libs.nordic.theme) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) implementation(libs.nordic.core) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index db0cedd7..b84003b2 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -38,8 +38,8 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher -import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.DefaultBleLogger import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.hts.view.TemperatureUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -59,7 +59,7 @@ class HTSRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: BlekLoggerAndLauncher? = null + private var logger: BleLoggerAndLauncher? = null private val _data = MutableStateFlow(HTSServiceData()) internal val data = _data.asStateFlow() @@ -88,7 +88,7 @@ class HTSRepository @Inject constructor( fun launch(device: ServerDevice) { _data.value = _data.value.copy(deviceName = device.name) - logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "HTS", device.address) + logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "HTS", device.address) serviceManager.startService(HTSService::class.java, device) } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 34da4057..921e736c 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -41,9 +41,8 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.client.main.connect -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -67,7 +66,7 @@ internal class HTSService : NotificationService() { @Inject lateinit var repository: HTSRepository - private lateinit var client: BleGattClient + private lateinit var client: ClientBleGatt override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -86,7 +85,7 @@ internal class HTSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@HTSService, logger = { p, s -> repository.log(p, s) }) + client = ClientBleGatt.connect(this@HTSService, device, logger = { p, s -> repository.log(p, s) }) client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } @@ -98,14 +97,15 @@ internal class HTSService : NotificationService() { return@launch } - client.discoverServices() - .filterNotNull() - .onEach { configureGatt(it) } - .catch { repository.onMissingServices() } - .launchIn(lifecycleScope) + try { + val services = client.discoverServices() + configureGatt(services) + } catch (e: Exception) { + repository.onMissingServices() + } } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: ClientBleGattServices) { val htsService = services.findService(HTS_SERVICE_UUID)!! val htsMeasurementCharacteristic = htsService.findCharacteristic(HTS_MEASUREMENT_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt index f1b7bcdf..4e6cf825 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSScreen.kt @@ -35,22 +35,20 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView -import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.hts.R import no.nordicsemi.android.hts.viewmodel.HTSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar -@OptIn(ExperimentalMaterial3Api::class) @Composable fun HTSScreen() { val viewModel: HTSViewModel = hiltViewModel() diff --git a/profile_prx/build.gradle.kts b/profile_prx/build.gradle.kts index 8c93e3b6..a4583611 100644 --- a/profile_prx/build.gradle.kts +++ b/profile_prx/build.gradle.kts @@ -53,7 +53,7 @@ dependencies { implementation(libs.nordic.ble.ktx) implementation(libs.nordic.theme) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) implementation(libs.nordic.core) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index f7ecdcf8..9d790899 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -38,8 +38,8 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher -import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.DefaultBleLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -58,7 +58,7 @@ class PRXRepository @Inject internal constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: BlekLoggerAndLauncher? = null + private var logger: BleLoggerAndLauncher? = null private val _data = MutableStateFlow(PRXServiceData()) internal val data = _data.asStateFlow() @@ -89,7 +89,7 @@ class PRXRepository @Inject internal constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "PRX", device.address) + logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "PRX", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(PRXService::class.java, device) } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index d98956e1..be7dd8ac 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -41,12 +41,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicBlekLogger -import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.client.main.connect -import no.nordicsemi.android.kotlin.ble.client.main.errors.GattOperationException -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectOptions import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus @@ -58,14 +55,13 @@ import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevelParser import no.nordicsemi.android.kotlin.ble.profile.prx.AlertLevelInputParser -import no.nordicsemi.android.kotlin.ble.server.main.BleGattServer -import no.nordicsemi.android.kotlin.ble.server.main.service.BleGattServerServiceType -import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristicConfig -import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattServiceConfig -import no.nordicsemi.android.kotlin.ble.server.main.service.BluetoothGattServerConnection +import no.nordicsemi.android.kotlin.ble.server.main.ServerBleGatt +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattCharacteristicConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattServiceConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattServiceType +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBluetoothGattConnection import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -84,10 +80,10 @@ internal class PRXService : NotificationService() { @Inject lateinit var repository: PRXRepository - private lateinit var client: BleGattClient - private lateinit var server: BleGattServer + private lateinit var client: ClientBleGatt + private lateinit var server: ServerBleGatt - private var alertLevelCharacteristic: BleGattCharacteristic? = null + private var alertLevelCharacteristic: ClientBleGattCharacteristic? = null override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -106,41 +102,41 @@ internal class PRXService : NotificationService() { } private fun startServer(device: ServerDevice) = lifecycleScope.launch { - val alertLevelCharacteristic = BleServerGattCharacteristicConfig( + val alertLevelCharacteristic = ServerBleGattCharacteristicConfig( uuid = ALERT_LEVEL_CHARACTERISTIC_UUID, properties = listOf(BleGattProperty.PROPERTY_WRITE_NO_RESPONSE), permissions = listOf(BleGattPermission.PERMISSION_WRITE) ) - val prxServiceConfig = BleServerGattServiceConfig( + val prxServiceConfig = ServerBleGattServiceConfig( uuid = PRX_SERVICE_UUID, - type = BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + type = ServerBleGattServiceType.SERVICE_TYPE_PRIMARY, characteristicConfigs = listOf(alertLevelCharacteristic) ) - val linkLossCharacteristic = BleServerGattCharacteristicConfig( + val linkLossCharacteristic = ServerBleGattCharacteristicConfig( uuid = ALERT_LEVEL_CHARACTERISTIC_UUID, properties = listOf(BleGattProperty.PROPERTY_WRITE, BleGattProperty.PROPERTY_READ), permissions = listOf(BleGattPermission.PERMISSION_WRITE, BleGattPermission.PERMISSION_READ), initialValue = AlertLevelInputParser.parse(AlarmLevel.HIGH) ) - val linkLossServiceConfig = BleServerGattServiceConfig( + val linkLossServiceConfig = ServerBleGattServiceConfig( uuid = LINK_LOSS_SERVICE_UUID, - type = BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + type =ServerBleGattServiceType.SERVICE_TYPE_PRIMARY, characteristicConfigs = listOf(linkLossCharacteristic) ) - server = BleGattServer.create(this@PRXService, prxServiceConfig, linkLossServiceConfig) + server = ServerBleGatt.create(this@PRXService, prxServiceConfig, linkLossServiceConfig) //Order is important. We don't want to connect before services have been added to the server. startGattClient(device) server.onNewConnection - .onEach { setUpServerConnection(it.second) } + .onEach { setUpServerConnection(it) } .launchIn(lifecycleScope) } - private fun setUpServerConnection(connection: BluetoothGattServerConnection) { + private fun setUpServerConnection(connection: ServerBluetoothGattConnection) { val prxService = connection.services.findService(PRX_SERVICE_UUID)!! val linkLossService = connection.services.findService(LINK_LOSS_SERVICE_UUID)!! @@ -159,8 +155,9 @@ internal class PRXService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect( + client = ClientBleGatt.connect( this@PRXService, + device, logger = { p, s -> repository.log(p, s) }, options = BleGattConnectOptions(autoConnect = true) ) @@ -177,18 +174,19 @@ internal class PRXService : NotificationService() { return@launch } - client.discoverServices() - .filterNotNull() - .onEach { configureGatt(it) } - .catch { repository.onMissingServices() } - .launchIn(lifecycleScope) + try { + val services = client.discoverServices() + configureGatt(services) + } catch (e: Exception) { + repository.onMissingServices() + } repository.remoteAlarmLevel .onEach { writeAlertLevel(it) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: ClientBleGattServices) { val prxService = services.findService(PRX_SERVICE_UUID)!! alertLevelCharacteristic = prxService.findCharacteristic(ALERT_LEVEL_CHARACTERISTIC_UUID)!! val linkLossService = services.findService(LINK_LOSS_SERVICE_UUID)!! diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt index 7fb84f6d..3f511756 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXScreen.kt @@ -42,15 +42,14 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView -import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.prx.R import no.nordicsemi.android.prx.viewmodel.PRXViewModel import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar -@OptIn(ExperimentalMaterial3Api::class) @Composable fun PRXScreen() { val viewModel: PRXViewModel = hiltViewModel() diff --git a/profile_rscs/build.gradle.kts b/profile_rscs/build.gradle.kts index 3f3f2a45..e1452d04 100644 --- a/profile_rscs/build.gradle.kts +++ b/profile_rscs/build.gradle.kts @@ -52,7 +52,7 @@ dependencies { implementation(libs.nordic.ble.ktx) implementation(libs.nordic.theme) - implementation(libs.nordic.uiscanner) + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) implementation(libs.nordic.core) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index d55efa3e..53861d4f 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -38,8 +38,8 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher -import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.DefaultBleLogger import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -58,7 +58,7 @@ class RSCSRepository @Inject constructor( private val serviceManager: ServiceManager, private val stringConst: StringConst ) { - private var logger: BlekLoggerAndLauncher? = null + private var logger: BleLoggerAndLauncher? = null private val _data = MutableStateFlow(RSCSServiceData()) internal val data = _data.asStateFlow() @@ -86,7 +86,7 @@ class RSCSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = NordicBlekLogger.create(context, stringConst.APP_NAME, "RSCS", device.address) + logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "RSCS", device.address) _data.value = _data.value.copy(deviceName = device.name) serviceManager.startService(RSCSService::class.java, device) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index 92ba9b37..7384411f 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -41,10 +41,8 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.logger.NordicBlekLogger -import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.client.main.connect -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -52,7 +50,6 @@ import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.android.ui.view.StringConst import java.util.* import javax.inject.Inject @@ -69,7 +66,7 @@ internal class RSCSService : NotificationService() { @Inject lateinit var repository: RSCSRepository - private lateinit var client: BleGattClient + private lateinit var client: ClientBleGatt override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -88,7 +85,7 @@ internal class RSCSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@RSCSService, logger = { p, s -> repository.log(p, s) }) + client = ClientBleGatt.connect(this@RSCSService, device, logger = { p, s -> repository.log(p, s) }) client.connectionStateWithStatus .onEach { repository.onConnectionStateChanged(it) } @@ -100,14 +97,15 @@ internal class RSCSService : NotificationService() { return@launch } - client.discoverServices() - .filterNotNull() - .onEach { configureGatt(it) } - .catch { repository.onMissingServices() } - .launchIn(lifecycleScope) + try { + val services = client.discoverServices() + configureGatt(services) + } catch (e: Exception) { + repository.onMissingServices() + } } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: ClientBleGattServices) { val rscsService = services.findService(RSCS_SERVICE_UUID)!! val rscsMeasurementCharacteristic = rscsService.findCharacteristic(RSC_MEASUREMENT_CHARACTERISTIC_UUID)!! val batteryService = services.findService(BATTERY_SERVICE_UUID)!! diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index a99e4800..486561c3 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -35,22 +35,20 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView -import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.rscs.R import no.nordicsemi.android.rscs.viewmodel.RSCSViewModel import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar -@OptIn(ExperimentalMaterial3Api::class) @Composable fun RSCSScreen() { val viewModel: RSCSViewModel = hiltViewModel() diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index bd8230c1..9f4973eb 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -72,10 +72,10 @@ dependencies { implementation(libs.nordic.ble.ktx) implementation(libs.nordic.theme) - implementation(libs.nordic.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) implementation(libs.nordic.core) + implementation(libs.nordic.blek.uiscanner) implementation(libs.androidx.dataStore.core) implementation(libs.androidx.dataStore.preferences) @@ -86,6 +86,7 @@ dependencies { implementation(libs.androidx.compose.material3) implementation(libs.androidx.activity.compose) implementation(libs.androidx.lifecycle.service) + implementation(libs.okio) // For Robolectric tests. testImplementation("com.google.dagger:hilt-android-testing:2.44") diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 961ef4b4..80e19783 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus @@ -65,7 +65,7 @@ class UARTRepository @Inject internal constructor( private val stringConst: StringConst, private val loggerFactory: NordicLoggerFactory ) { - private var logger: BlekLoggerAndLauncher? = null + private var logger: BleLoggerAndLauncher? = null private val _data = MutableStateFlow(UARTServiceData()) internal val data = _data.asStateFlow() diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 40812a05..06be469e 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -41,10 +41,10 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.kotlin.ble.client.main.callback.BleGattClient -import no.nordicsemi.android.kotlin.ble.client.main.connect -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattCharacteristic -import no.nordicsemi.android.kotlin.ble.client.main.service.BleGattServices +import no.nordicsemi.android.common.core.DataByteArray +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty import no.nordicsemi.android.kotlin.ble.core.data.BleWriteType @@ -69,7 +69,7 @@ internal class UARTService : NotificationService() { @Inject lateinit var repository: UARTRepository - private lateinit var client: BleGattClient + private lateinit var client: ClientBleGatt override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) @@ -88,7 +88,7 @@ internal class UARTService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - client = device.connect(this@UARTService, logger = { p, s -> repository.log(p, s) }) + client = ClientBleGatt.connect(this@UARTService, device, logger = { p, s -> repository.log(p, s) }) client.requestMtu(Mtu.max) @@ -101,14 +101,15 @@ internal class UARTService : NotificationService() { return@launch } - client.discoverServices() - .filterNotNull() - .onEach { configureGatt(it) } - .catch { repository.onMissingServices() } - .launchIn(lifecycleScope) + try { + val services = client.discoverServices() + configureGatt(services) + } catch (e: Exception) { + repository.onMissingServices() + } } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: ClientBleGattServices) { val uartService = services.findService(UART_SERVICE_UUID)!! val rxCharacteristic = uartService.findCharacteristic(UART_RX_CHARACTERISTIC_UUID)!! val txCharacteristic = uartService.findCharacteristic(UART_TX_CHARACTERISTIC_UUID)!! @@ -122,19 +123,19 @@ internal class UARTService : NotificationService() { ?.launchIn(lifecycleScope) txCharacteristic.getNotifications() - .onEach { repository.onNewMessageReceived(String(it)) } - .onEach { repository.log(10, "Received: ${String(it)}") } + .onEach { repository.onNewMessageReceived(String(it.value)) } + .onEach { repository.log(10, "Received: ${String(it.value)}") } .catch { it.printStackTrace() } .launchIn(lifecycleScope) repository.command - .onEach { rxCharacteristic.splitWrite(it.toByteArray(), getWriteType(rxCharacteristic)) } + .onEach { rxCharacteristic.splitWrite(DataByteArray.from(it), getWriteType(rxCharacteristic)) } .onEach { repository.onNewMessageSent(it) } .onEach { repository.log(10, "Sent: $it") } .launchIn(lifecycleScope) } - private fun getWriteType(characteristic: BleGattCharacteristic): BleWriteType { + private fun getWriteType(characteristic: ClientBleGattCharacteristic): BleWriteType { return if (characteristic.properties.contains(BleGattProperty.PROPERTY_WRITE)) { BleWriteType.DEFAULT } else { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index 71355fc3..62a3d5fe 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -48,16 +48,14 @@ import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.theme.view.PagerView import no.nordicsemi.android.common.theme.view.PagerViewEntity import no.nordicsemi.android.common.theme.view.PagerViewItem -import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView -import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView -import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.viewmodel.UARTViewModel import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView -@OptIn(ExperimentalMaterial3Api::class) @Composable fun UARTScreen() { val viewModel: UARTViewModel = hiltViewModel() diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt index 18a53331..8799220f 100644 --- a/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt @@ -5,7 +5,7 @@ import dagger.Module import dagger.Provides import dagger.hilt.components.SingletonComponent import dagger.hilt.testing.TestInstallIn -import no.nordicsemi.android.common.logger.BlekLoggerAndLauncher +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher import no.nordicsemi.android.ui.view.NordicLoggerFactory import no.nordicsemi.android.ui.view.NordicLoggerFactoryHiltModule @@ -24,8 +24,8 @@ class NordicLoggerFactoryTestModule { profile: String?, key: String, name: String?, - ): BlekLoggerAndLauncher { - return object : BlekLoggerAndLauncher { + ): BleLoggerAndLauncher { + return object : BleLoggerAndLauncher { override fun launch() { } diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt index fcedf25a..353130a0 100644 --- a/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt @@ -24,16 +24,16 @@ import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import no.nordicsemi.android.analytics.AppAnalytics -import no.nordicsemi.android.common.logger.NordicBlekLogger +import no.nordicsemi.android.common.core.ApplicationScope +import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.DefaultBleLogger import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.di.NavigationModule -import no.nordicsemi.android.kotlin.ble.client.main.ClientScope import no.nordicsemi.android.kotlin.ble.core.MockServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus -import no.nordicsemi.android.kotlin.ble.server.main.ServerScope import no.nordicsemi.android.uart.data.UARTPersistentDataSource import no.nordicsemi.android.uart.repository.UARTRepository import no.nordicsemi.android.uart.view.DisconnectEvent @@ -84,7 +84,7 @@ internal class UARTViewModelTest { lateinit var context: Context @RelaxedMockK - lateinit var logger: NordicBlekLogger + lateinit var logger: BleLoggerAndLauncher @Inject lateinit var repository: UARTRepository @@ -119,16 +119,14 @@ internal class UARTViewModelTest { profile: String?, key: String, name: String?, - ): NordicBlekLogger { + ): BleLoggerAndLauncher { return logger } }) runBlocking { - mockkStatic("no.nordicsemi.android.kotlin.ble.client.main.ClientScopeKt") - every { ClientScope } returns CoroutineScope(UnconfinedTestDispatcher()) - mockkStatic("no.nordicsemi.android.kotlin.ble.server.main.ServerScopeKt") - every { ServerScope } returns CoroutineScope(UnconfinedTestDispatcher()) + mockkStatic("no.nordicsemi.android.common.core.ApplicationScopeKt") + every { ApplicationScope } returns CoroutineScope(UnconfinedTestDispatcher()) every { stringConst.APP_NAME } returns "Test" uartServer = UartServer(CoroutineScope(UnconfinedTestDispatcher())) @@ -138,8 +136,8 @@ internal class UARTViewModelTest { @Before fun prepareLogger() { - mockkObject(NordicBlekLogger.Companion) - every { NordicBlekLogger.create(any(), any(), any(), any()) } returns mockk() + mockkObject(DefaultBleLogger.Companion) + every { DefaultBleLogger.create(any(), any(), any(), any()) } returns mockk() } @Test diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..352aedb7 --- /dev/null +++ b/renovate.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base", + "group:all", + ":dependencyDashboard", + "schedule:daily" + ], + "commitMessageExtra": "{{{currentValue}}} to {{#if isPinDigest}}{{{newDigestShort}}}{{else}}{{#if isMajor}}{{prettyNewMajor}}{{else}}{{#if isSingleVersion}}{{prettyNewVersion}}{{else}}{{#if newValue}}{{{newValue}}}{{else}}{{{newDigestShort}}}{{/if}}{{/if}}{{/if}}{{/if}}", + "packageRules": [ + { + "matchPackagePatterns": [ + "androidx.compose.compiler:compiler" + ], + "groupName": "kotlin" + }, + { + "matchPackagePatterns": [ + "org.jetbrains.kotlin.*" + ], + "groupName": "kotlin" + }, + { + "matchPackagePatterns": [ + "com.google.devtools.ksp" + ], + "groupName": "kotlin" + } + ] +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 771d7b99..9a194bf5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -48,11 +48,11 @@ dependencyResolutionManagement { maven(url = "https://androidx.dev/storage/compose-compiler/repository/") maven(url = "https://jitpack.io") } - versionCatalogs { - create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.5.8") - } - } +// versionCatalogs { +// create("libs") { +// from("no.nordicsemi.android.gradle:version-catalog:1.8.0") +// } +// } } rootProject.name = "Android-nRF-Toolbox" From 09f570e4fad1b5bfb9647d4752f6d146e080a9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Fri, 4 Aug 2023 13:02:35 +0200 Subject: [PATCH 093/101] Fix build issue. --- .../nrftoolbox/NrfToolboxApplication.kt | 2 +- .../android/{gls => uart}/UartServer.kt | 87 +++++++++---------- .../android/gls/UARTViewModelTest.kt | 1 + 3 files changed, 45 insertions(+), 45 deletions(-) rename profile_uart/src/debug/java/no/nordicsemi/android/{gls => uart}/UartServer.kt (57%) diff --git a/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt b/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt index 7919cd5d..9e0ae893 100644 --- a/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt +++ b/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt @@ -35,7 +35,7 @@ import android.app.Application import dagger.hilt.android.HiltAndroidApp import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.AppOpenEvent -import no.nordicsemi.android.gls.UartServer +import no.nordicsemi.android.uart.UartServer import javax.inject.Inject @HiltAndroidApp diff --git a/profile_uart/src/debug/java/no/nordicsemi/android/gls/UartServer.kt b/profile_uart/src/debug/java/no/nordicsemi/android/uart/UartServer.kt similarity index 57% rename from profile_uart/src/debug/java/no/nordicsemi/android/gls/UartServer.kt rename to profile_uart/src/debug/java/no/nordicsemi/android/uart/UartServer.kt index bf041074..ecba01fb 100644 --- a/profile_uart/src/debug/java/no/nordicsemi/android/gls/UartServer.kt +++ b/profile_uart/src/debug/java/no/nordicsemi/android/uart/UartServer.kt @@ -1,26 +1,26 @@ -package no.nordicsemi.android.gls +package no.nordicsemi.android.uart import android.annotation.SuppressLint import android.content.Context import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.advertiser.BleAdvertiser import no.nordicsemi.android.kotlin.ble.core.MockServerDevice -import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertiseConfig +import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertisingConfig import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser -import no.nordicsemi.android.kotlin.ble.server.main.BleGattServer -import no.nordicsemi.android.kotlin.ble.server.main.service.BleGattServerServiceType -import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristic -import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattCharacteristicConfig -import no.nordicsemi.android.kotlin.ble.server.main.service.BleServerGattServiceConfig -import no.nordicsemi.android.kotlin.ble.server.main.service.BluetoothGattServerConnection +import no.nordicsemi.android.kotlin.ble.server.main.ServerBleGatt +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattCharacteristic +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattCharacteristicConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattServiceConfig +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattServiceType +import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBluetoothGattConnection import no.nordicsemi.android.uart.repository.BATTERY_LEVEL_CHARACTERISTIC_UUID import no.nordicsemi.android.uart.repository.BATTERY_SERVICE_UUID import no.nordicsemi.android.uart.repository.UART_RX_CHARACTERISTIC_UUID @@ -37,27 +37,27 @@ class UartServer @Inject constructor( private val scope: CoroutineScope ) { - lateinit var server: BleGattServer + lateinit var server: ServerBleGatt - lateinit var glsCharacteristic: BleServerGattCharacteristic - lateinit var glsContextCharacteristic: BleServerGattCharacteristic - lateinit var racpCharacteristic: BleServerGattCharacteristic - lateinit var batteryLevelCharacteristic: BleServerGattCharacteristic + lateinit var glsCharacteristic: ServerBleGattCharacteristic + lateinit var glsContextCharacteristic: ServerBleGattCharacteristic + lateinit var racpCharacteristic: ServerBleGattCharacteristic + lateinit var batteryLevelCharacteristic: ServerBleGattCharacteristic - private var lastRequest = byteArrayOf() + private var lastRequest = DataByteArray() - val YOUNGEST_RECORD = byteArrayOf(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11) - val OLDEST_RECORD = byteArrayOf(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11) + val YOUNGEST_RECORD = DataByteArray.from(0x07, 0x00, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x05, 0x00, 0x00, 0x26, 0xD2.toByte(), 0x11) + val OLDEST_RECORD = DataByteArray.from(0x07, 0x04, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x11, 0x00, 0x00, 0x82.toByte(), 0xD2.toByte(), 0x11) val records = listOf( YOUNGEST_RECORD, - byteArrayOf(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11), - byteArrayOf(0x07, 0x02, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0B, 0x00, 0x00, 0x54, 0xD2.toByte(), 0x11), - byteArrayOf(0x07, 0x03, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0E, 0x00, 0x00, 0x6B, 0xD2.toByte(), 0x11), + DataByteArray.from(0x07, 0x01, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x08, 0x00, 0x00, 0x3D, 0xD2.toByte(), 0x11), + DataByteArray.from(0x07, 0x02, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0B, 0x00, 0x00, 0x54, 0xD2.toByte(), 0x11), + DataByteArray.from(0x07, 0x03, 0x00, 0xDC.toByte(), 0x07, 0x01, 0x01, 0x0C, 0x1E, 0x0E, 0x00, 0x00, 0x6B, 0xD2.toByte(), 0x11), OLDEST_RECORD ) - val racp = byteArrayOf(0x06, 0x00, 0x01, 0x01) + val racp = DataByteArray.from(0x06, 0x00, 0x01, 0x01) fun start( context: Context, @@ -66,44 +66,44 @@ class UartServer @Inject constructor( address = "55:44:33:22:11" ), ) = scope.launch { - val rxCharacteristic = BleServerGattCharacteristicConfig( + val rxCharacteristic = ServerBleGattCharacteristicConfig( UART_RX_CHARACTERISTIC_UUID, listOf(BleGattProperty.PROPERTY_NOTIFY), listOf() ) - val txCharacteristic = BleServerGattCharacteristicConfig( + val txCharacteristic = ServerBleGattCharacteristicConfig( UART_TX_CHARACTERISTIC_UUID, listOf(BleGattProperty.PROPERTY_INDICATE, BleGattProperty.PROPERTY_WRITE), listOf(BleGattPermission.PERMISSION_WRITE) ) - val uartService = BleServerGattServiceConfig( + val uartService = ServerBleGattServiceConfig( UART_SERVICE_UUID, - BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + ServerBleGattServiceType.SERVICE_TYPE_PRIMARY, listOf(rxCharacteristic, txCharacteristic) ) - val batteryLevelCharacteristic = BleServerGattCharacteristicConfig( + val batteryLevelCharacteristic = ServerBleGattCharacteristicConfig( BATTERY_LEVEL_CHARACTERISTIC_UUID, listOf(BleGattProperty.PROPERTY_READ, BleGattProperty.PROPERTY_NOTIFY), listOf(BleGattPermission.PERMISSION_READ) ) - val batteryService = BleServerGattServiceConfig( + val batteryService = ServerBleGattServiceConfig( BATTERY_SERVICE_UUID, - BleGattServerServiceType.SERVICE_TYPE_PRIMARY, + ServerBleGattServiceType.SERVICE_TYPE_PRIMARY, listOf(batteryLevelCharacteristic) ) - server = BleGattServer.create( + server = ServerBleGatt.create( context = context, config = arrayOf(uartService, batteryService), mock = device ) val advertiser = BleAdvertiser.create(context) - advertiser.advertise(config = BleAdvertiseConfig(), mock = device).launchIn(scope) + advertiser.advertise(config = BleAdvertisingConfig(), mock = device).launchIn(scope) launch { server.connections @@ -116,7 +116,7 @@ class UartServer @Inject constructor( server.stopServer() } - private fun setUpConnection(connection: BluetoothGattServerConnection) { + private fun setUpConnection(connection: ServerBluetoothGattConnection) { // val glsService = connection.services.findService(GLS_SERVICE_UUID)!! // glsCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CHARACTERISTIC)!! // glsContextCharacteristic = glsService.findCharacteristic(GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC)!! @@ -130,9 +130,8 @@ class UartServer @Inject constructor( // startBatteryService(connection) } - private fun startGlsService(connection: BluetoothGattServerConnection) { + private fun startGlsService(connection: ServerBluetoothGattConnection) { racpCharacteristic.value - .filter { it.isNotEmpty() } .onEach { lastRequest = it } .launchIn(scope) } @@ -141,42 +140,42 @@ class UartServer @Inject constructor( sendResponse(lastRequest) } - private fun sendResponse(request: ByteArray) { - if (request.contentEquals(RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords().value)) { + private fun sendResponse(request: DataByteArray) { + if (request == RecordAccessControlPointInputParser.reportNumberOfAllStoredRecords()) { sendAll(glsCharacteristic) racpCharacteristic.setValue(racp) - } else if (request.contentEquals(RecordAccessControlPointInputParser.reportLastStoredRecord().value)) { + } else if (request == RecordAccessControlPointInputParser.reportLastStoredRecord()) { sendLast(glsCharacteristic) racpCharacteristic.setValue(racp) - } else if (request.contentEquals(RecordAccessControlPointInputParser.reportFirstStoredRecord().value)) { + } else if (request == RecordAccessControlPointInputParser.reportFirstStoredRecord()) { sendFirst(glsCharacteristic) racpCharacteristic.setValue(racp) } } - private fun sendFirst(characteristics: BleServerGattCharacteristic) { + private fun sendFirst(characteristics: ServerBleGattCharacteristic) { characteristics.setValue(records.first()) } - private fun sendLast(characteristics: BleServerGattCharacteristic) { + private fun sendLast(characteristics: ServerBleGattCharacteristic) { characteristics.setValue(records.last()) } - private fun sendAll(characteristics: BleServerGattCharacteristic) = scope.launch { + private fun sendAll(characteristics: ServerBleGattCharacteristic) = scope.launch { records.forEach { characteristics.setValue(it) delay(100) } } - private fun startBatteryService(connection: BluetoothGattServerConnection) { + private fun startBatteryService(connection: ServerBluetoothGattConnection) { scope.launch { repeat(100) { - batteryLevelCharacteristic.setValue(byteArrayOf(0x61)) + batteryLevelCharacteristic.setValue(DataByteArray.from(0x61)) delay(STANDARD_DELAY) - batteryLevelCharacteristic.setValue(byteArrayOf(0x60)) + batteryLevelCharacteristic.setValue(DataByteArray.from(0x60)) delay(STANDARD_DELAY) - batteryLevelCharacteristic.setValue(byteArrayOf(0x5F)) + batteryLevelCharacteristic.setValue(DataByteArray.from(0x5F)) delay(STANDARD_DELAY) } } diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt index 353130a0..211d8b43 100644 --- a/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt @@ -34,6 +34,7 @@ import no.nordicsemi.android.kotlin.ble.core.MockServerDevice import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus +import no.nordicsemi.android.uart.UartServer import no.nordicsemi.android.uart.data.UARTPersistentDataSource import no.nordicsemi.android.uart.repository.UARTRepository import no.nordicsemi.android.uart.view.DisconnectEvent From f0bc8ac3c843b5fbc72d9b78b98ab977539d7681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Fri, 4 Aug 2023 14:00:43 +0200 Subject: [PATCH 094/101] Remove unused wire --- profile_uart/build.gradle.kts | 5 -- .../android/uart/data/MacroSerializer.kt | 56 ------------------- profile_uart/src/main/proto/macro.proto | 18 ------ 3 files changed, 79 deletions(-) delete mode 100644 profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroSerializer.kt delete mode 100644 profile_uart/src/main/proto/macro.proto diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index 9f4973eb..1ad55d32 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -33,7 +33,6 @@ plugins { alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) alias(libs.plugins.ksp) - alias(libs.plugins.wire) } android { @@ -44,10 +43,6 @@ android { } } -wire { - kotlin {} -} - dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroSerializer.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroSerializer.kt deleted file mode 100644 index f42e91be..00000000 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroSerializer.kt +++ /dev/null @@ -1,56 +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.uart.data - -import androidx.datastore.core.CorruptionException -import androidx.datastore.core.Serializer -import androidx.datastore.preferences.protobuf.InvalidProtocolBufferException -import no.nordicsemi.android.MacroSettings -import java.io.InputStream -import java.io.OutputStream - -object MacroSerializer : Serializer { - override val defaultValue: MacroSettings = MacroSettings() - - override suspend fun readFrom(input: InputStream): MacroSettings { - try { - return MacroSettings.ADAPTER.decode(input) - } catch (exception: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", exception) - } - } - - override suspend fun writeTo( - t: MacroSettings, - output: OutputStream - ) = MacroSettings.ADAPTER.encode(output, t) -} diff --git a/profile_uart/src/main/proto/macro.proto b/profile_uart/src/main/proto/macro.proto deleted file mode 100644 index 6265d4b5..00000000 --- a/profile_uart/src/main/proto/macro.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; - -option java_package = "no.nordicsemi.android"; -option java_multiple_files = true; - -message Macro { - string name = 1; - enum NewLineType { - LF = 0; - LF_CR = 1; - CR = 2; - } - NewLineType newLineType = 2; -} - -message MacroSettings { - repeated Macro macros = 1; -} From 44fd13186a54b79a48f95f34cf00f31a0c682f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Fri, 4 Aug 2023 14:07:34 +0200 Subject: [PATCH 095/101] Fix Gradle --- lib_service/build.gradle.kts | 1 + profile_uart/build.gradle.kts | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib_service/build.gradle.kts b/lib_service/build.gradle.kts index 4227f919..c8c164b1 100644 --- a/lib_service/build.gradle.kts +++ b/lib_service/build.gradle.kts @@ -40,6 +40,7 @@ android { dependencies { implementation(project(":lib_ui")) + implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.ktx) implementation(libs.nordic.blek.uiscanner) diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index 1ad55d32..11a568c9 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -83,12 +83,9 @@ dependencies { implementation(libs.androidx.lifecycle.service) implementation(libs.okio) - // For Robolectric tests. - testImplementation("com.google.dagger:hilt-android-testing:2.44") - // ...with Kotlin. - kaptTest("com.google.dagger:hilt-android-compiler:2.46.1") - - testImplementation("androidx.test:rules:1.5.0") + testImplementation(libs.hilt.android.testing) + kaptTest(libs.hilt.compiler) + testImplementation(libs.androidx.test.rules) testImplementation(libs.junit4) testImplementation(libs.test.mockk) From 86bec097ed6bf374a12d7edf688872e4624fafa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Fri, 4 Aug 2023 15:12:20 +0200 Subject: [PATCH 096/101] Bump version catalog --- gradle/libs.versions.toml | 238 ---------------------------------- profile_uart/build.gradle.kts | 1 - settings.gradle.kts | 10 +- 3 files changed, 5 insertions(+), 244 deletions(-) delete mode 100644 gradle/libs.versions.toml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml deleted file mode 100644 index 2841082a..00000000 --- a/gradle/libs.versions.toml +++ /dev/null @@ -1,238 +0,0 @@ -[versions] -accompanist = "0.30.1" -androidDesugarJdkLibs = "2.0.3" -androidGradlePlugin = "8.1.0" -androidxActivity = "1.7.2" -androidxAnnotation = "1.6.0" -androidxAppCompat = "1.6.1" -androidxComposeBom = "2023.06.01" # https://developer.android.com/jetpack/compose/bom/bom-mapping -androidxComposeCompiler = "1.5.1" # https://developer.android.com/jetpack/androidx/releases/compose-kotlin -androidxComposeRuntimeTracing = "1.0.0-alpha03" -androidxCore = "1.10.1" -androidxCoreSplashscreen = "1.0.1" -androidxDataStore = "1.0.0" -androidxEspresso = "3.5.1" -androidxHiltNavigationCompose = "1.0.0" -androidxLifecycle = "2.6.1" -androidxLocalbroadcastmanager = "1.1.0" -androidxMacroBenchmark = "1.1.1" -androidxNavigation = "2.6.0" -androidxMetrics = "1.0.0-alpha04" -androidxProfileinstaller = "1.3.1" -androidxStartup = "1.1.1" -androidxWindowManager = "1.1.0" -androidxTestCore = "1.5.0" -androidxTestExt = "1.1.5" -androidxTestRunner = "1.5.2" -androidxTestRules = "1.5.0" -androidxTracing = "1.1.0" -androidxUiAutomator = "2.2.0" -androidxWork = "2.8.1" -coil = "2.4.0" -firebaseBom = "32.2.0" -hilt = "2.47" -hiltExt = "1.0.0" -jacoco = "0.8.7" -junit4 = "4.13.2" -kotlin = "1.9.0" -kotlinxCoroutines = "1.7.3" -kotlinxDatetime = "0.4.0" -kotlinxSerializationJson = "1.5.1" -ksp = "1.9.0-1.0.12" -lint = "31.1.0" -memfault-cloud = "2.0.5" -okhttp = "4.11.0" -protobuf = "3.23.4" -protobufPlugin = "0.9.4" -publishPlugin = "1.2.0" -retrofit = "2.9.0" -retrofitKotlinxSerializationJson = "1.0.0" -room = "2.5.2" -secrets = "2.0.1" -turbine = "1.0.0" -markdown = "0.3.0" -wirePlugin = "4.7.2" -timber = "5.0.1" -chart = "v3.1.0" -leakcanary = "2.12" -mockk = "1.13.5" -slf4j = "2.0.7" -robolectric = "4.10.3" -skydovesBallon = "1.5.4" -moshiKotlin = "1.15.0" -moshiAdapters = "1.15.0" -okio = "3.4.0" - -nordic-blek = "0.2.1" -nordic-ble = "2.6.1" -nordic-dfu = "2.3.0" -nordic-log = "2.3.0" -nordic-mcumgr = "1.7.0" -nordic-scanner = "1.6.0" -nordic-common = "1.8.0" -nordic-memfault = "1.0.2" -nordicPlugins = "1.7.1" -dokkaPlugin = "1.8.20" -googleServicesPlugins = "4.3.15" -firebaseCrashlyticsPlugins = "2.9.2" - -[libraries] -accompanist-flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanist" } -accompanist-swiperefresh = { group = "com.google.accompanist", name = "accompanist-swiperefresh", version.ref = "accompanist" } -accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" } -accompanist-pager = { group = "com.google.accompanist", name = "accompanist-pager", version.ref = "accompanist" } -accompanist-pagerindicators = { group = "com.google.accompanist", name = "accompanist-pager-indicators", version.ref = "accompanist" } -accompanist-placeholder = { group = "com.google.accompanist", name = "accompanist-placeholder-material", version.ref = "accompanist" } -android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } -androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } -androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidxAnnotation" } -androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } -androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" } -androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } -androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } -androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } -androidx-compose-material = { group = "androidx.compose.material", name = "material" } -androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" } -androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } -androidx-compose-material3-windowSizeClass = {group = "androidx.compose.material3", name = "material3-window-size-class" } -androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" } -androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata" } -androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" } -androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } -androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" } -androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } -androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } -androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } -androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util" } -androidx-core = { group = "androidx.core", name = "core", version.ref = "androidxCore" } -androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" } -androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" } -androidx-dataStore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" } -androidx-dataStore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "androidxDataStore" } -androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } -androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidxLifecycle" } -androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidxLifecycle" } -androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime", version.ref = "androidxLifecycle" } -androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } -androidx-lifecycle-service = { group = "androidx.lifecycle", name = "lifecycle-service", version.ref = "androidxLifecycle" } -androidx-lifecycle-viewModel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" } -androidx-lifecycle-viewModel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } -androidx-lifecycle-viewModel-savedState = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-savedstate", version.ref = "androidxLifecycle" } -androidx-localbroadcastmanager = { group = "androidx.localbroadcastmanager", name = "localbroadcastmanager", version.ref = "androidxLocalbroadcastmanager" } -androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", version.ref = "androidxMetrics" } -androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" } -androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "androidxNavigation" } -androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "androidxProfileinstaller" } -androidx-startup = { group = "androidx.startup", name = "startup-runtime", version.ref = "androidxStartup" } -androidx-window-manager = {module = "androidx.window:window", version.ref = "androidxWindowManager"} -androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" } -androidx-test-ext = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxTestExt" } -androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxEspresso" } -androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTestRunner" } -androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTestRules" } -androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "androidxUiAutomator" } -androidx-tracing-ktx = {group = "androidx.tracing", name="tracing-ktx", version.ref = "androidxTracing" } -androidx-work-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "androidxWork" } -androidx-work-testing = { group = "androidx.work", name = "work-testing", version.ref = "androidxWork" } -coil-kt = { group = "io.coil-kt", name = "coil", version.ref = "coil"} -coil-kt-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil"} -coil-kt-svg = { group = "io.coil-kt", name = "coil-svg", version.ref = "coil"} -firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom"} -firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics-ktx" } -firebase-database = { group = "com.google.firebase", name = "firebase-database-ktx" } -firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics-ktx" } -hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } -hilt-ext-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hiltExt" } -hilt-ext-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hiltExt" } -hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } -hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" } -junit4 = { group = "junit", name = "junit", version.ref = "junit4" } -kotlin-junit = { group = "org.jetbrains.kotlin", name = "kotlin-test-junit", version.ref = "kotlin" } -kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" } -kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } -kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } -kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } -kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } -kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } -lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "lint" } -okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } -protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } -protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } -turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" } -retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } -retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version.ref = "retrofitKotlinxSerializationJson" } -room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } -room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } -room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } -markdown = { group = "com.github.jeziellago", name = "compose-markdown", version.ref = "markdown" } -memfault-cloud = { group = "com.memfault.cloud", name = "cloud-android", version.ref = "memfault-cloud" } -timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" } -chart = { group = "com.github.PhilJay", name = "MPAndroidChart", version.ref = "chart" } -leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary" } -test-slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" } -test-mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" } -test-robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } -skydoves-ballon = {group = "com.github.skydoves", name = "balloon-compose", version.ref = "skydovesBallon"} -moshi-kotlin = {group = "com.squareup.moshi", name = "moshi-kotlin", version.ref = "moshiKotlin"} -moshi-adapters = {group = "com.squareup.moshi", name = "moshi-adapters", version.ref = "moshiAdapters"} -okio = {group = "com.squareup.okio", name = "okio", version.ref = "okio"} - -# Nordic -nordic-ble-ktx = { group = "no.nordicsemi.android", name = "ble-ktx", version.ref = "nordic-ble" } -nordic-ble-common = { group = "no.nordicsemi.android", name = "ble-common", version.ref = "nordic-ble" } -nordic-dfu = { group = "no.nordicsemi.android", name = "dfu", version.ref = "nordic-dfu" } -nordic-mcumgr-core = { group = "no.nordicsemi.android", name = "mcumgr-core", version.ref = "nordic-mcumgr" } -nordic-mcumgr-ble = { group = "no.nordicsemi.android", name = "mcumgr-ble", version.ref = "nordic-mcumgr" } -nordic-log = { group = "no.nordicsemi.android", name = "log", version.ref = "nordic-log" } -nordic-log-timber = { group = "no.nordicsemi.android", name = "log-timber", version.ref = "nordic-log" } -nordic-scanner = { group = "no.nordicsemi.android.support.v18", name = "scanner", version.ref = "nordic-scanner" } -nordic-core = { group = "no.nordicsemi.android.common", name = "core", version.ref = "nordic-common" } -nordic-theme = { group = "no.nordicsemi.android.common", name = "theme", version.ref = "nordic-common" } -nordic-analytics = { group = "no.nordicsemi.android.common", name = "analytics", version.ref = "nordic-common" } -nordic-navigation = { group = "no.nordicsemi.android.common", name = "navigation", version.ref = "nordic-common" } -nordic-uilogger = { group = "no.nordicsemi.android.common", name = "uilogger", version.ref = "nordic-common" } -nordic-logger = { group = "no.nordicsemi.android.common", name = "logger", version.ref = "nordic-common" } -nordic-permissions-nfc = { group = "no.nordicsemi.android.common", name = "permissions-nfc", version.ref = "nordic-common" } -nordic-permissions-ble = { group = "no.nordicsemi.android.common", name = "permissions-ble", version.ref = "nordic-common" } -nordic-permissions-internet = { group = "no.nordicsemi.android.common", name = "permissions-internet", version.ref = "nordic-common" } -nordic-memfault = { group = "no.nordicsemi.android", name = "memfault", version.ref = "nordic-memfault" } - -# BleK -nordic-blek-client = { group = "no.nordicsemi.android.kotlin.ble", name = "client", version.ref = "nordic-blek" } -nordic-blek-server = { group = "no.nordicsemi.android.kotlin.ble", name = "server", version.ref = "nordic-blek" } -nordic-blek-profile = { group = "no.nordicsemi.android.kotlin.ble", name = "profile", version.ref = "nordic-blek" } -nordic-blek-core = { group = "no.nordicsemi.android.kotlin.ble", name = "core", version.ref = "nordic-blek" } -nordic-blek-scanner = { group = "no.nordicsemi.android.kotlin.ble", name = "scanner", version.ref = "nordic-blek" } -nordic-blek-advertiser = { group = "no.nordicsemi.android.kotlin.ble", name = "advertiser", version.ref = "nordic-blek" } -nordic-blek-uiscanner = { group = "no.nordicsemi.android.kotlin.ble", name = "uiscanner", version.ref = "nordic-blek" } - -# Dependencies of the included build-logic -android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } -kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } - -[plugins] -nordic-application = { id = "no.nordicsemi.android.gradle.application", version.ref = "nordicPlugins" } -nordic-application-compose = { id = "no.nordicsemi.android.gradle.application.compose", version.ref = "nordicPlugins" } -nordic-library = { id = "no.nordicsemi.android.gradle.library", version.ref = "nordicPlugins" } -nordic-library-compose = { id = "no.nordicsemi.android.gradle.library.compose", version.ref = "nordicPlugins" } -nordic-feature = { id = "no.nordicsemi.android.gradle.feature", version.ref = "nordicPlugins" } -nordic-hilt = { id = "no.nordicsemi.android.gradle.hilt", version.ref = "nordicPlugins" } -nordic-kotlin = { id = "no.nordicsemi.android.gradle.kotlin", version.ref = "nordicPlugins" } -nordic-nexus = { id = "no.nordicsemi.android.gradle.nexus", version.ref = "nordicPlugins" } -android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } -android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } -android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" } -ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } -kotlin-dokka = { id = "org.jetbrains.dokka", version.ref = "dokkaPlugin" } -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } -kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } -kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } -protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } -secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } -publish = { id = "com.gradle.plugin-publish", version.ref = "publishPlugin" } -wire = { id = "com.squareup.wire", version.ref = "wirePlugin" } -google-services = { id = "com.google.gms.google-services", version.ref = "googleServicesPlugins" } -firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugins" } diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index 11a568c9..bca8be56 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -81,7 +81,6 @@ dependencies { implementation(libs.androidx.compose.material3) implementation(libs.androidx.activity.compose) implementation(libs.androidx.lifecycle.service) - implementation(libs.okio) testImplementation(libs.hilt.android.testing) kaptTest(libs.hilt.compiler) diff --git a/settings.gradle.kts b/settings.gradle.kts index 9a194bf5..64d771a4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -48,11 +48,11 @@ dependencyResolutionManagement { maven(url = "https://androidx.dev/storage/compose-compiler/repository/") maven(url = "https://jitpack.io") } -// versionCatalogs { -// create("libs") { -// from("no.nordicsemi.android.gradle:version-catalog:1.8.0") -// } -// } + versionCatalogs { + create("libs") { + from("no.nordicsemi.android.gradle:version-catalog:1.8.2") + } + } } rootProject.name = "Android-nRF-Toolbox" From 0212f49b526050b5b90c7d1b06ed1f656b076d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Sat, 5 Aug 2023 09:14:47 +0200 Subject: [PATCH 097/101] Apply fixes --- .../no/nordicsemi/android/nrftoolbox/view/FeatureButton.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/FeatureButton.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/FeatureButton.kt index 3708b895..8d280648 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/FeatureButton.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/FeatureButton.kt @@ -44,6 +44,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text @@ -61,6 +62,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.nrftoolbox.R +@OptIn(ExperimentalMaterial3Api::class) @Composable fun FeatureButton( @DrawableRes iconId: Int, @@ -70,7 +72,7 @@ fun FeatureButton( @StringRes description: Int? = null, onClick: () -> Unit ) { - OutlinedCard(modifier = Modifier.clickable { onClick() }) { + OutlinedCard(onClick = onClick) { Row( modifier = Modifier.padding(16.dp).fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, From f650e0c8f6cfc23ed9a56164baf314fe6453ed0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Sun, 6 Aug 2023 10:09:42 +0200 Subject: [PATCH 098/101] Add fixes --- .../java/no/nordicsemi/android/cgms/viewmodel/CGMViewModel.kt | 1 + 1 file changed, 1 insertion(+) 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 a06eb650..67c09256 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 @@ -116,6 +116,7 @@ internal class CGMViewModel @Inject constructor( private fun disconnect() { repository.disconnect() + navigationManager.navigateUp() } override fun onCleared() { From 5ec41b98b0e772df19b7b57f143a277623e93474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Sun, 6 Aug 2023 10:29:24 +0200 Subject: [PATCH 099/101] Fix not cleaning log session --- .../nordicsemi/android/uart/repository/UARTService.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 06be469e..1981537b 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -46,8 +46,10 @@ import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.data.BleGattConnectionStatus import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty import no.nordicsemi.android.kotlin.ble.core.data.BleWriteType +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.Mtu import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.service.DEVICE_DATA @@ -93,7 +95,9 @@ internal class UARTService : NotificationService() { client.requestMtu(Mtu.max) client.connectionStateWithStatus + .filterNotNull() .onEach { repository.onConnectionStateChanged(it) } + .onEach { stopIfDisconnected(it.state, it.status) } .filterNotNull() .launchIn(lifecycleScope) @@ -143,6 +147,13 @@ internal class UARTService : NotificationService() { } } + private fun stopIfDisconnected(connectionState: GattConnectionState, connectionStatus: BleGattConnectionStatus) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED && !connectionStatus.isLinkLoss) { + repository.disconnect() + stopSelf() + } + } + private fun disconnect() { client.disconnect() } From 4e3d76e67499354561e23039876dc82067c5afbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Sun, 6 Aug 2023 10:51:22 +0200 Subject: [PATCH 100/101] Observer connection status after successfull service discovery --- .../android/uart/repository/UARTService.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index 1981537b..d1496fee 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -92,25 +92,25 @@ internal class UARTService : NotificationService() { private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { client = ClientBleGatt.connect(this@UARTService, device, logger = { p, s -> repository.log(p, s) }) - client.requestMtu(Mtu.max) - - client.connectionStateWithStatus - .filterNotNull() - .onEach { repository.onConnectionStateChanged(it) } - .onEach { stopIfDisconnected(it.state, it.status) } - .filterNotNull() - .launchIn(lifecycleScope) - if (!client.isConnected) { return@launch } + client.requestMtu(Mtu.max) + try { val services = client.discoverServices() configureGatt(services) } catch (e: Exception) { repository.onMissingServices() } + + client.connectionStateWithStatus + .filterNotNull() + .onEach { repository.onConnectionStateChanged(it) } + .onEach { stopIfDisconnected(it.state, it.status) } + .filterNotNull() + .launchIn(lifecycleScope) } private suspend fun configureGatt(services: ClientBleGattServices) { From 878bf61e52ccc14b5a55f7ad017a8cd4045c3bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Sun, 6 Aug 2023 11:59:11 +0200 Subject: [PATCH 101/101] Small fixes --- .../nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt b/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt index 9e0ae893..2d8a9f81 100644 --- a/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt +++ b/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt @@ -35,6 +35,7 @@ import android.app.Application import dagger.hilt.android.HiltAndroidApp import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.AppOpenEvent +import no.nordicsemi.android.gls.GlsServer import no.nordicsemi.android.uart.UartServer import javax.inject.Inject @@ -45,7 +46,7 @@ class NrfToolboxApplication : Application() { lateinit var analytics: AppAnalytics @Inject - lateinit var glsServer: UartServer + lateinit var glsServer: GlsServer @Inject lateinit var uartServer: UartServer @@ -54,7 +55,5 @@ class NrfToolboxApplication : Application() { super.onCreate() analytics.logEvent(AppOpenEvent) - - uartServer.start(this) } }