From 00d70863deab28ddf334526308f1487799e89b83 Mon Sep 17 00:00:00 2001 From: Sylwester Zielinski Date: Fri, 20 Jan 2023 14:15:35 +0100 Subject: [PATCH] Migrate profiles to new libraries --- app/build.gradle.kts | 1 + .../android/nrftoolbox/AppDestination.kt | 49 ++++--- .../android/nrftoolbox/MainActivity.kt | 12 +- .../android/nrftoolbox/view/HomeView.kt | 28 ++-- .../nrftoolbox/viewmodel/HomeViewModel.kt | 14 +- build.gradle.kts | 3 + gradle/wrapper/gradle-wrapper.properties | 2 +- lib_analytics/build.gradle.kts | 2 +- .../android/analytics/AppAnalytics.kt | 2 +- lib_scanner/build.gradle.kts | 48 +++++++ .../scanner/ExampleInstrumentedTest.kt | 24 ++++ lib_scanner/src/main/AndroidManifest.xml | 4 + .../toolbox/scanner/ScannerDestination.kt | 29 ++++ .../toolbox/scanner/ExampleUnitTest.kt | 17 +++ lib_service/build.gradle.kts | 3 +- .../android/service/ServiceManager.kt | 2 +- lib_ui/build.gradle.kts | 2 +- .../nordicsemi/android/ui/view/BackButton.kt | 14 ++ .../android/ui/view/BatteryLevelView.kt | 4 +- .../nordicsemi/android/ui/view/TopAppBar.kt | 4 + lib_utils/build.gradle.kts | 1 + .../java/no/nordicsemi/android/utils/Ext.kt | 10 -- profile_bps/build.gradle.kts | 4 +- .../nordicsemi/android/bps/data/BPSManager.kt | 2 +- .../android/bps/repository/BPSRepository.kt | 11 +- .../nordicsemi/android/bps/view/BPSScreen.kt | 31 +++-- .../android/bps/view/BPSSensorsReadingView.kt | 4 +- .../android/bps/viewmodel/BPSViewModel.kt | 41 +++--- profile_cgms/build.gradle.kts | 6 +- .../android/cgms/data/CGMManager.kt | 3 +- .../no/nordicsemi/android/cgms/data/Ext.kt | 3 +- .../android/cgms/repository/CGMRepository.kt | 10 +- .../android/cgms/repository/CGMService.kt | 2 +- .../android/cgms/view/CGMContentView.kt | 6 +- .../nordicsemi/android/cgms/view/CGMScreen.kt | 31 +++-- .../android/cgms/viewmodel/CGMViewModel.kt | 50 ++++--- profile_csc/build.gradle.kts | 4 +- .../nordicsemi/android/csc/data/CSCManager.kt | 2 +- .../android/csc/repository/CSCRepository.kt | 10 +- .../android/csc/repository/CSCService.kt | 2 +- .../android/csc/view/CSCContentView.kt | 9 +- .../nordicsemi/android/csc/view/CSCMappers.kt | 4 +- .../nordicsemi/android/csc/view/CSCScreen.kt | 33 +++-- .../android/csc/view/SelectWheelSizeDialog.kt | 2 +- .../android/csc/view/SensorsReadingView.kt | 4 +- .../android/csc/viewmodel/CSCViewModel.kt | 33 +++-- profile_gls/build.gradle.kts | 4 +- .../nordicsemi/android/gls/GLSDestination.kt | 12 +- .../nordicsemi/android/gls/data/GLSManager.kt | 2 +- .../nordicsemi/android/gls/data/GLSRecord.kt | 4 +- .../android/gls/details/view/Field.kt | 2 +- .../gls/details/view/GLSDetailsContentView.kt | 4 +- .../details/viewmodel/GLSDetailsViewModel.kt | 18 +-- .../android/gls/main/view/GLSContentView.kt | 6 +- .../android/gls/main/view/GLSScreen.kt | 31 +++-- .../gls/main/viewmodel/GLSViewModel.kt | 36 +++-- .../android/gls/repository/GLSRepository.kt | 13 +- profile_hrs/build.gradle.kts | 4 +- .../nordicsemi/android/hrs/data/HRSManager.kt | 2 +- .../android/hrs/service/HRSRepository.kt | 16 ++- .../android/hrs/service/HRSService.kt | 2 +- .../android/hrs/view/HRSContentView.kt | 4 +- .../nordicsemi/android/hrs/view/HRSScreen.kt | 31 +++-- .../android/hrs/viewmodel/HRSViewModel.kt | 48 ++++--- profile_hts/build.gradle.kts | 4 +- .../nordicsemi/android/hts/data/HTSManager.kt | 2 +- .../android/hts/repository/HTSRepository.kt | 16 ++- .../android/hts/repository/HTSService.kt | 2 +- .../android/hts/view/HTSContentView.kt | 8 +- .../nordicsemi/android/hts/view/HTSMapper.kt | 4 +- .../nordicsemi/android/hts/view/HTSScreen.kt | 31 +++-- .../android/hts/viewmodel/HTSViewModel.kt | 47 ++++--- profile_prx/build.gradle.kts | 4 +- .../nordicsemi/android/prx/data/PRXManager.kt | 2 +- .../android/prx/repository/PRXRepository.kt | 10 +- .../android/prx/repository/PRXService.kt | 2 +- .../android/prx/view/PRXContentView.kt | 6 +- .../android/prx/view/PRXLinkLossView.kt | 4 +- .../nordicsemi/android/prx/view/PRXScreen.kt | 31 +++-- .../android/prx/viewmodel/PRXViewModel.kt | 33 +++-- profile_rscs/build.gradle.kts | 4 +- .../android/rscs/data/RSCSManager.kt | 2 +- .../android/rscs/repository/RSCSRepository.kt | 16 ++- .../android/rscs/view/RSCSScreen.kt | 31 +++-- .../android/rscs/view/SensorsReadingView.kt | 4 +- .../android/rscs/viewmodel/RSCSViewModel.kt | 47 ++++--- profile_uart/build.gradle.kts | 9 +- .../android/uart/data/UARTManager.kt | 2 +- ...Configuration.java => XmlConfiguration.kt} | 82 +++++------ .../nordicsemi/android/uart/db/XmlMacro.java | 127 ------------------ .../no/nordicsemi/android/uart/db/XmlMacro.kt | 102 ++++++++++++++ .../android/uart/repository/UARTRepository.kt | 23 +++- .../android/uart/repository/UARTService.kt | 2 +- .../android/uart/view/InputSection.kt | 10 +- .../android/uart/view/MacroSection.kt | 3 +- .../uart/view/UARTAddConfigurationDialog.kt | 1 + .../android/uart/view/UARTAddMacroDialog.kt | 7 +- .../uart/view/UARTConfigurationPicker.kt | 3 +- .../android/uart/view/UARTContentView.kt | 2 +- .../android/uart/view/UARTScreen.kt | 55 ++++---- .../android/uart/viewmodel/UARTViewModel.kt | 72 +++++++--- settings.gradle.kts | 3 +- 102 files changed, 960 insertions(+), 674 deletions(-) create mode 100644 lib_scanner/build.gradle.kts create mode 100644 lib_scanner/src/androidTest/java/no/nordicsemi/android/toolbox/scanner/ExampleInstrumentedTest.kt create mode 100644 lib_scanner/src/main/AndroidManifest.xml create mode 100644 lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt create mode 100644 lib_scanner/src/test/java/no/nordicsemi/android/toolbox/scanner/ExampleUnitTest.kt create mode 100644 lib_ui/src/main/java/no/nordicsemi/android/ui/view/BackButton.kt rename profile_uart/src/main/java/no/nordicsemi/android/uart/db/{XmlConfiguration.java => XmlConfiguration.kt} (56%) delete mode 100644 profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.java create mode 100644 profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9d423206..cb993657 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -60,6 +60,7 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(libs.nordic.core) implementation(libs.nordic.theme) diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppDestination.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppDestination.kt index 80e22832..d76a3e6d 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppDestination.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppDestination.kt @@ -33,34 +33,43 @@ package no.nordicsemi.android.nrftoolbox import no.nordicsemi.android.bps.view.BPSScreen import no.nordicsemi.android.cgms.view.CGMScreen +import no.nordicsemi.android.common.navigation.createSimpleDestination +import no.nordicsemi.android.common.navigation.defineDestination import no.nordicsemi.android.csc.view.CSCScreen import no.nordicsemi.android.gls.main.view.GLSScreen import no.nordicsemi.android.hrs.view.HRSScreen import no.nordicsemi.android.hts.view.HTSScreen -import no.nordicsemi.android.navigation.ComposeDestination -import no.nordicsemi.android.navigation.ComposeDestinations import no.nordicsemi.android.nrftoolbox.view.HomeScreen import no.nordicsemi.android.prx.view.PRXScreen import no.nordicsemi.android.rscs.view.RSCSScreen +import no.nordicsemi.android.toolbox.scanner.ScannerDestination import no.nordicsemi.android.uart.view.UARTScreen -import no.nordicsemi.ui.scanner.navigation.view.FindDeviceScreen -val HomeDestinations = ComposeDestinations(HomeDestination.values().map { it.destination }) -val ProfileDestinations = ComposeDestinations(ProfileDestination.values().map { it.destination }) +val HomeDestinationId = createSimpleDestination("home-destination") -enum class HomeDestination(val destination: ComposeDestination) { - HOME(ComposeDestination("home-destination") { HomeScreen() }), - SCANNER(ComposeDestination("scanner-destination") { FindDeviceScreen() }); -} +val HomeDestinations = listOf( + defineDestination(HomeDestinationId) { HomeScreen() }, + ScannerDestination +) -enum class ProfileDestination(val destination: ComposeDestination) { - CSC(ComposeDestination("csc-destination") { CSCScreen() }), - HRS(ComposeDestination("hrs-destination") { HRSScreen() }), - HTS(ComposeDestination("hts-destination") { HTSScreen() }), - GLS(ComposeDestination("gls-destination") { GLSScreen() }), - BPS(ComposeDestination("bps-destination") { BPSScreen() }), - PRX(ComposeDestination("prx-destination") { PRXScreen() }), - RSCS(ComposeDestination("rscs-destination") { RSCSScreen() }), - CGMS(ComposeDestination("cgms-destination") { CGMScreen() }), - UART(ComposeDestination("uart-destination") { UARTScreen() }); -} +val CSCDestinationId = createSimpleDestination("csc-destination") +val HRSDestinationId = createSimpleDestination("hrs-destination") +val HTSDestinationId = createSimpleDestination("hts-destination") +val GLSDestinationId = createSimpleDestination("gls-destination") +val BPSDestinationId = createSimpleDestination("bps-destination") +val PRXDestinationId = createSimpleDestination("prx-destination") +val RSCSDestinationId = createSimpleDestination("rscs-destination") +val CGMSDestinationId = createSimpleDestination("cgms-destination") +val UARTDestinationId = createSimpleDestination("uart-destination") + +val ProfileDestinations = listOf( + defineDestination(CSCDestinationId) { CSCScreen() }, + defineDestination(HRSDestinationId) { HRSScreen() }, + defineDestination(HTSDestinationId) { HTSScreen() }, + defineDestination(GLSDestinationId) { GLSScreen() }, + defineDestination(BPSDestinationId) { BPSScreen() }, + defineDestination(PRXDestinationId) { PRXScreen() }, + defineDestination(RSCSDestinationId) { RSCSScreen() }, + defineDestination(CGMSDestinationId) { CGMScreen() }, + defineDestination(UARTDestinationId) { UARTScreen() }, +) diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/MainActivity.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/MainActivity.kt index 0e09f57b..d24ee4b5 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/MainActivity.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/MainActivity.kt @@ -38,12 +38,16 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.ui.Modifier import dagger.hilt.android.AndroidEntryPoint -import no.nordicsemi.analytics.view.AnalyticsPermissionRequestDialog +import no.nordicsemi.android.common.analytics.view.AnalyticsPermissionRequestDialog +import no.nordicsemi.android.common.navigation.NavigationView +import no.nordicsemi.android.common.theme.NordicActivity +import no.nordicsemi.android.common.theme.NordicTheme +import no.nordicsemi.android.gls.GLSDestination import no.nordicsemi.android.gls.GLSDestinations -import no.nordicsemi.android.theme.NordicActivity -import no.nordicsemi.android.theme.NordicTheme import no.nordicsemi.android.navigation.NavigationView import no.nordicsemi.android.nrftoolbox.repository.ActivitySignals +import no.nordicsemi.android.theme.NordicTheme +import no.nordicsemi.android.toolbox.scanner.ScannerDestination import no.nordicsemi.ui.scanner.ScannerDestinations import javax.inject.Inject @@ -62,7 +66,7 @@ class MainActivity : NordicActivity() { color = MaterialTheme.colorScheme.surface, modifier = Modifier.fillMaxSize() ) { - NavigationView(HomeDestinations + ProfileDestinations + ScannerDestinations + GLSDestinations) + NavigationView(HomeDestinations + ProfileDestinations + ScannerDestination + GLSDestination) } AnalyticsPermissionRequestDialog() diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeView.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeView.kt index 5f9b3452..dd7f4a33 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeView.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/HomeView.kt @@ -48,9 +48,17 @@ import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.analytics.Link import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileOpenEvent +import no.nordicsemi.android.nrftoolbox.BPSDestinationId import no.nordicsemi.android.nrftoolbox.BuildConfig -import no.nordicsemi.android.nrftoolbox.ProfileDestination +import no.nordicsemi.android.nrftoolbox.CGMSDestinationId +import no.nordicsemi.android.nrftoolbox.CSCDestinationId +import no.nordicsemi.android.nrftoolbox.GLSDestinationId +import no.nordicsemi.android.nrftoolbox.HRSDestinationId +import no.nordicsemi.android.nrftoolbox.HTSDestinationId +import no.nordicsemi.android.nrftoolbox.PRXDestinationId import no.nordicsemi.android.nrftoolbox.R +import no.nordicsemi.android.nrftoolbox.RSCSDestinationId +import no.nordicsemi.android.nrftoolbox.UARTDestinationId import no.nordicsemi.android.nrftoolbox.viewmodel.HomeViewModel private const val DFU_PACKAGE_NAME = "no.nordicsemi.android.dfu" @@ -87,14 +95,14 @@ fun HomeScreen() { Spacer(modifier = Modifier.height(16.dp)) FeatureButton(R.drawable.ic_gls, R.string.gls_module, R.string.gls_module_full) { - viewModel.openProfile(ProfileDestination.GLS) + viewModel.openProfile(GLSDestinationId) viewModel.logEvent(ProfileOpenEvent(Profile.GLS)) } Spacer(modifier = Modifier.height(16.dp)) FeatureButton(R.drawable.ic_bps, R.string.bps_module, R.string.bps_module_full) { - viewModel.openProfile(ProfileDestination.BPS) + viewModel.openProfile(BPSDestinationId) viewModel.logEvent(ProfileOpenEvent(Profile.BPS)) } @@ -109,42 +117,42 @@ fun HomeScreen() { Spacer(modifier = Modifier.height(16.dp)) FeatureButton(R.drawable.ic_csc, R.string.csc_module, R.string.csc_module_full, state.isCSCModuleRunning) { - viewModel.openProfile(ProfileDestination.CSC) + viewModel.openProfile(CSCDestinationId) viewModel.logEvent(ProfileOpenEvent(Profile.CSC)) } Spacer(modifier = Modifier.height(16.dp)) FeatureButton(R.drawable.ic_hrs, R.string.hrs_module, R.string.hrs_module_full, state.isHRSModuleRunning) { - viewModel.openProfile(ProfileDestination.HRS) + viewModel.openProfile(HRSDestinationId) viewModel.logEvent(ProfileOpenEvent(Profile.HRS)) } Spacer(modifier = Modifier.height(16.dp)) FeatureButton(R.drawable.ic_hts, R.string.hts_module, R.string.hts_module_full, state.isHTSModuleRunning) { - viewModel.openProfile(ProfileDestination.HTS) + viewModel.openProfile(HTSDestinationId) viewModel.logEvent(ProfileOpenEvent(Profile.HTS)) } Spacer(modifier = Modifier.height(16.dp)) FeatureButton(R.drawable.ic_rscs, R.string.rscs_module, R.string.rscs_module_full, state.isRSCSModuleRunning) { - viewModel.openProfile(ProfileDestination.RSCS) + viewModel.openProfile(RSCSDestinationId) viewModel.logEvent(ProfileOpenEvent(Profile.RSCS)) } Spacer(modifier = Modifier.height(16.dp)) FeatureButton(R.drawable.ic_cgm, R.string.cgm_module, R.string.cgm_module_full, state.isCGMModuleRunning) { - viewModel.openProfile(ProfileDestination.CGMS) + viewModel.openProfile(CGMSDestinationId) viewModel.logEvent(ProfileOpenEvent(Profile.CGMS)) } Spacer(modifier = Modifier.height(16.dp)) FeatureButton(R.drawable.ic_prx, R.string.prx_module, R.string.prx_module_full, state.isPRXModuleRunning) { - viewModel.openProfile(ProfileDestination.PRX) + viewModel.openProfile(PRXDestinationId) viewModel.logEvent(ProfileOpenEvent(Profile.PRX)) } @@ -159,7 +167,7 @@ fun HomeScreen() { Spacer(modifier = Modifier.height(16.dp)) FeatureButton(R.drawable.ic_uart, R.string.uart_module, R.string.uart_module_full, state.isUARTModuleRunning) { - viewModel.openProfile(ProfileDestination.UART) + viewModel.openProfile(UARTDestinationId) viewModel.logEvent(ProfileOpenEvent(Profile.UART)) } 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 1ec15f3a..9d1063ba 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 @@ -41,12 +41,12 @@ 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.navigation.DestinationId import no.nordicsemi.android.csc.repository.CSCRepository import no.nordicsemi.android.hrs.service.HRSRepository import no.nordicsemi.android.hts.repository.HTSRepository -import no.nordicsemi.android.logger.LoggerAppRunner -import no.nordicsemi.android.navigation.NavigationManager -import no.nordicsemi.android.nrftoolbox.ProfileDestination +import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.nrftoolbox.repository.ActivitySignals import no.nordicsemi.android.nrftoolbox.view.HomeViewState import no.nordicsemi.android.prx.repository.PRXRepository @@ -56,7 +56,7 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val activitySignals: ActivitySignals, cgmRepository: CGMRepository, cscRepository: CSCRepository, @@ -65,7 +65,6 @@ class HomeViewModel @Inject constructor( prxRepository: PRXRepository, rscsRepository: RSCSRepository, uartRepository: UARTRepository, - private val loggerAppRunner: LoggerAppRunner, private val analytics: AppAnalytics ) : ViewModel() { @@ -106,11 +105,12 @@ class HomeViewModel @Inject constructor( }.launchIn(viewModelScope) } - fun openProfile(destination: ProfileDestination) { - navigationManager.navigateTo(destination.destination.id) + fun openProfile(destination: DestinationId) { + navigationManager.navigateTo(destination) } fun openLogger() { + NordicLogger.Companion.launch() loggerAppRunner.runLogger() } diff --git a/build.gradle.kts b/build.gradle.kts index e8a17fd3..7d7ad855 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,7 @@ plugins { alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.kapt) apply true alias(libs.plugins.kotlin.serialization) apply false + alias(libs.plugins.kotlin.parcelize) apply false alias(libs.plugins.hilt) apply false alias(libs.plugins.secrets) apply false alias(libs.plugins.protobuf) apply false @@ -44,4 +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.android.library") version "7.4.0" apply false + id("org.jetbrains.kotlin.android") version "1.7.21" apply false } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b49ef06f..8c2e0703 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-rc-2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/lib_analytics/build.gradle.kts b/lib_analytics/build.gradle.kts index d18fe157..5f5d090a 100644 --- a/lib_analytics/build.gradle.kts +++ b/lib_analytics/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) } android { diff --git a/lib_analytics/src/main/java/no/nordicsemi/android/analytics/AppAnalytics.kt b/lib_analytics/src/main/java/no/nordicsemi/android/analytics/AppAnalytics.kt index 31fc67ce..3eef0c38 100644 --- a/lib_analytics/src/main/java/no/nordicsemi/android/analytics/AppAnalytics.kt +++ b/lib_analytics/src/main/java/no/nordicsemi/android/analytics/AppAnalytics.kt @@ -32,7 +32,7 @@ package no.nordicsemi.android.analytics import android.annotation.SuppressLint -import no.nordicsemi.analytics.NordicAnalytics +import no.nordicsemi.android.common.analytics.NordicAnalytics import javax.inject.Inject import javax.inject.Singleton diff --git a/lib_scanner/build.gradle.kts b/lib_scanner/build.gradle.kts new file mode 100644 index 00000000..44b2e683 --- /dev/null +++ b/lib_scanner/build.gradle.kts @@ -0,0 +1,48 @@ +/* + * 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. + */ + +plugins { + alias(libs.plugins.nordic.feature) +} + +android { + namespace = "no.nordicsemi.android.toolbox.scanner" +} + +dependencies { + implementation(libs.nordic.uiscanner) + implementation(libs.nordic.navigation) + + implementation(libs.androidx.compose.material.iconsExtended) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.activity.compose) +} 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 new file mode 100644 index 00000000..b0a898f8 --- /dev/null +++ b/lib_scanner/src/androidTest/java/no/nordicsemi/android/toolbox/scanner/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package no.nordicsemi.android.toolbox.scanner + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * 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/AndroidManifest.xml b/lib_scanner/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/lib_scanner/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ 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 new file mode 100644 index 00000000..da5cf239 --- /dev/null +++ b/lib_scanner/src/main/java/no/nordicsemi/android/toolbox/scanner/ScannerDestination.kt @@ -0,0 +1,29 @@ +package no.nordicsemi.android.toolbox.scanner + +import android.os.ParcelUuid +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.common.ui.scanner.model.DiscoveredBluetoothDevice + +val ScannerDestinationId = createDestination("uiscanner-destination") + +val ScannerDestination = defineDestination(ScannerDestinationId) { + val navigationViewModel = hiltViewModel() + + val arg = navigationViewModel.parameterOf(ScannerDestinationId) + + ScannerScreen( + uuid = arg, + onResult = { + when (it) { + is DeviceSelected -> navigationViewModel.navigateUpWithResult(ScannerDestinationId, it.device) + ScanningCancelled -> navigationViewModel.navigateUp() + } + } + ) +} 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 new file mode 100644 index 00000000..b79161ad --- /dev/null +++ b/lib_scanner/src/test/java/no/nordicsemi/android/toolbox/scanner/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package no.nordicsemi.android.toolbox.scanner + +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) + } +} \ No newline at end of file diff --git a/lib_service/build.gradle.kts b/lib_service/build.gradle.kts index 5c1c3ee1..a58d8cce 100644 --- a/lib_service/build.gradle.kts +++ b/lib_service/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) } @@ -46,4 +46,5 @@ dependencies { 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 befa48f9..f2753e6f 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.ui.scanner.DiscoveredBluetoothDevice +import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import javax.inject.Inject const val DEVICE_DATA = "device-data" diff --git a/lib_ui/build.gradle.kts b/lib_ui/build.gradle.kts index 81dcb564..86970f79 100644 --- a/lib_ui/build.gradle.kts +++ b/lib_ui/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) } android { diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/BackButton.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/BackButton.kt new file mode 100644 index 00000000..524d6045 --- /dev/null +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/BackButton.kt @@ -0,0 +1,14 @@ +package no.nordicsemi.android.ui.view + +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import no.nordicsemi.android.ui.R + +@Composable +fun NavigateUpButton(navigateUp: () -> Unit) { + Button(onClick = { navigateUp() }) { + Text(text = stringResource(id = R.string.go_up)) + } +} diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/BatteryLevelView.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/BatteryLevelView.kt index 4a56e96e..e1a8dc76 100644 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/BatteryLevelView.kt +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/BatteryLevelView.kt @@ -31,14 +31,14 @@ package no.nordicsemi.android.ui.view +import androidx.compose.material3.OutlinedCard import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import no.nordicsemi.android.theme.ScreenSection import no.nordicsemi.android.ui.R @Composable fun BatteryLevelView(batteryLevel: Int) { - ScreenSection { + OutlinedCard { KeyValueField( stringResource(id = R.string.field_battery), "$batteryLevel%" 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 a3b33864..b89a039e 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 @@ -45,6 +45,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import no.nordicsemi.android.ui.R +@OptIn(ExperimentalMaterial3Api::class) @Composable fun CloseIconAppBar(text: String, onClick: () -> Unit) { SmallTopAppBar( @@ -67,6 +68,7 @@ fun CloseIconAppBar(text: String, onClick: () -> Unit) { ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun LoggerBackIconAppBar(text: String, onClick: () -> Unit) { SmallTopAppBar( @@ -100,6 +102,7 @@ fun LoggerBackIconAppBar(text: String, onClick: () -> Unit) { ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun BackIconAppBar(text: String, onClick: () -> Unit) { SmallTopAppBar( @@ -123,6 +126,7 @@ fun BackIconAppBar(text: String, onClick: () -> Unit) { ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun LoggerIconAppBar(text: String, onClick: () -> Unit, onDisconnectClick: () -> Unit, onLoggerClick: () -> Unit) { SmallTopAppBar( diff --git a/lib_utils/build.gradle.kts b/lib_utils/build.gradle.kts index 979ea938..4e00c465 100644 --- a/lib_utils/build.gradle.kts +++ b/lib_utils/build.gradle.kts @@ -41,4 +41,5 @@ android { dependencies { implementation(libs.nordic.navigation) implementation(libs.nordic.uiscanner) + implementation(libs.kotlinx.coroutines.core) } 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 2dfa1b6a..07901c4a 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 @@ -38,12 +38,6 @@ import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import no.nordicsemi.android.navigation.ParcelableArgument -import no.nordicsemi.android.navigation.SuccessDestinationResult -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice - -val T.exhaustive - get() = this val String.Companion.EMPTY get() = "" @@ -54,10 +48,6 @@ fun Context.isServiceRunning(serviceClassName: String): Boolean { return services.find { it.service.className == serviceClassName } != null } -fun SuccessDestinationResult.getDevice(): DiscoveredBluetoothDevice { - return (argument as ParcelableArgument).value as DiscoveredBluetoothDevice -} - private val exceptionHandler = CoroutineExceptionHandler { _, t -> Log.e("COROUTINE-EXCEPTION", "Uncaught exception", t) } diff --git a/profile_bps/build.gradle.kts b/profile_bps/build.gradle.kts index e6228d36..c0d4a0da 100644 --- a/profile_bps/build.gradle.kts +++ b/profile_bps/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) } @@ -41,6 +41,7 @@ android { dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(project(":lib_ui")) implementation(project(":lib_utils")) @@ -53,6 +54,7 @@ dependencies { implementation(libs.nordic.uiscanner) implementation(libs.androidx.compose.material.iconsExtended) + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) implementation(libs.androidx.activity.compose) 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 index 7afb1547..93b3a8ca 100644 --- 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 @@ -45,7 +45,7 @@ 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.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.service.ConnectionObserverAdapter import java.util.* 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 72d6f1bf..527a4db8 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 @@ -44,11 +44,11 @@ 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.logger.NordicLogger -import no.nordicsemi.android.logger.NordicLoggerFactory +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.service.BleManagerResult import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject @ViewModelScoped @@ -62,7 +62,7 @@ internal class BPSRepository @Inject constructor( private var logger: NordicLogger? = null fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow> = callbackFlow { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "BPS", device.address()).also { + val createdLogger = loggerFactory.create(stringConst.APP_NAME, "BPS", device.address).also { logger = it } val manager = BPSManager(context, scope, createdLogger) @@ -93,7 +93,6 @@ internal class BPSRepository @Inject constructor( } fun openLogger() { - logger?.openLogger() + NordicLogger.launch(context, logger) } - } 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 633ef514..21020d64 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 @@ -41,14 +41,21 @@ import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.bps.R import no.nordicsemi.android.bps.viewmodel.BPSViewModel -import no.nordicsemi.android.service.* +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.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.ui.scanner.ui.DeviceConnectingView -import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView -import no.nordicsemi.ui.scanner.ui.NoDeviceView -import no.nordicsemi.ui.scanner.ui.Reason +import no.nordicsemi.android.ui.view.NavigateUpButton @Composable fun BPSScreen() { @@ -62,18 +69,18 @@ fun BPSScreen() { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { when (state) { - NoDeviceState -> NoDeviceView() + NoDeviceState -> DeviceConnectingView() is WorkingState -> when (state.result) { is IdleResult, is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp) - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp) - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp) + is 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) } } - }.exhaustive + } } } } 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 c7af1d87..939ba192 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,6 +34,7 @@ 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.OutlinedCard import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -41,14 +42,13 @@ 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.theme.ScreenSection import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.SectionTitle @Composable internal fun BPSSensorsReadingView(state: BPSData) { - ScreenSection { + OutlinedCard { Column { SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.bps_records)) Spacer(modifier = Modifier.height(16.dp)) 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 802d902f..3f79cd58 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,6 +31,7 @@ package no.nordicsemi.android.bps.viewmodel +import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -43,19 +44,23 @@ 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.* -import no.nordicsemi.android.navigation.* +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.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.android.utils.getDevice -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice -import no.nordicsemi.ui.scanner.ScannerDestinationId +import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @HiltViewModel internal class BPSViewModel @Inject constructor( private val repository: BPSRepository, - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { @@ -63,27 +68,25 @@ internal class BPSViewModel @Inject constructor( val state = _state.asStateFlow() init { - navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(BPS_SERVICE_UUID)) + navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(BPS_SERVICE_UUID)) - navigationManager.recentResult.onEach { - if (it.destinationId == ScannerDestinationId) { - handleArgs(it) - } - }.launchIn(viewModelScope) + navigationManager.resultFrom(ScannerDestinationId) + .onEach { handleArgs(it) } + .launchIn(viewModelScope) } - private fun handleArgs(args: DestinationResult) { - when (args) { - is CancelDestinationResult -> navigationManager.navigateUp() - is SuccessDestinationResult -> connectDevice(args.getDevice()) - }.exhaustive + private fun handleArgs(result: NavigationResult) { + when (result) { + is NavigationResult.Cancelled -> navigationManager.navigateUp() + is NavigationResult.Success -> connectDevice(result.value) + } } fun onEvent(event: BPSViewEvent) { when (event) { DisconnectEvent -> navigationManager.navigateUp() OpenLoggerEvent -> repository.openLogger() - }.exhaustive + } } private fun connectDevice(device: DiscoveredBluetoothDevice) { diff --git a/profile_cgms/build.gradle.kts b/profile_cgms/build.gradle.kts index a462d51b..4c107eff 100644 --- a/profile_cgms/build.gradle.kts +++ b/profile_cgms/build.gradle.kts @@ -30,8 +30,8 @@ */ plugins { - alias(libs.plugins.nordic.library) - alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.nordic.feature) + alias(libs.plugins.kotlin.parcelize) } android { @@ -41,6 +41,7 @@ android { dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(project(":lib_ui")) implementation(project(":lib_utils")) @@ -52,6 +53,7 @@ dependencies { implementation(libs.nordic.uiscanner) implementation(libs.nordic.navigation) + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) 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 index 3198f81c..55c1189e 100644 --- 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 @@ -53,8 +53,7 @@ import no.nordicsemi.android.ble.common.profile.cgm.CGMSpecificOpsControlPointCa 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.cgms.repository.toList -import no.nordicsemi.android.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.service.ConnectionObserverAdapter import no.nordicsemi.android.utils.launchWithCatch import java.util.* 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 index 0d357cf3..80861747 100644 --- 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 @@ -29,11 +29,10 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.cgms.repository +package no.nordicsemi.android.cgms.data import android.util.SparseArray import androidx.core.util.keyIterator -import no.nordicsemi.android.cgms.data.CGMRecord internal fun SparseArray.toList(): List { val list = mutableListOf() 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 14068842..7ce30d1b 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 @@ -39,13 +39,13 @@ 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.logger.NordicLogger -import no.nordicsemi.android.logger.NordicLoggerFactory +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.service.BleManagerResult import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject import javax.inject.Singleton @@ -71,7 +71,7 @@ class CGMRepository @Inject constructor( } fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CGMS", device.address()).also { + val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CGMS", device.address).also { logger = it } val manager = CGMManager(context, scope, createdLogger) @@ -110,7 +110,7 @@ class CGMRepository @Inject constructor( } fun openLogger() { - logger?.openLogger() + NordicLogger.launch(context, logger) } fun release() { 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 100f966e..853516d3 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,9 +36,9 @@ 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.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject @AndroidEntryPoint 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 e4a90c22..f484d241 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,6 +38,7 @@ import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -49,7 +50,6 @@ import no.nordicsemi.android.cgms.data.CGMData import no.nordicsemi.android.cgms.data.CGMRecord import no.nordicsemi.android.cgms.data.CGMServiceCommand import no.nordicsemi.android.cgms.data.RequestStatus -import no.nordicsemi.android.theme.ScreenSection import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.SectionTitle @@ -87,7 +87,7 @@ internal fun CGMContentView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) { @Composable private fun SettingsView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) { - ScreenSection { + OutlinedCard { SectionTitle(icon = Icons.Default.Settings, title = "Request items") Spacer(modifier = Modifier.height(16.dp)) @@ -115,7 +115,7 @@ private fun SettingsView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) { @Composable private fun RecordsView(state: CGMData) { - ScreenSection { + OutlinedCard { if (state.records.isEmpty()) { RecordsViewWithoutData() } else { 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 eb017be7..aebd9d1e 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 @@ -41,14 +41,21 @@ import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.cgms.R import no.nordicsemi.android.cgms.viewmodel.CGMViewModel -import no.nordicsemi.android.service.* +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.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.ui.scanner.ui.DeviceConnectingView -import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView -import no.nordicsemi.ui.scanner.ui.NoDeviceView -import no.nordicsemi.ui.scanner.ui.Reason +import no.nordicsemi.android.ui.view.NavigateUpButton @Composable fun CGMScreen() { @@ -62,18 +69,18 @@ fun CGMScreen() { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { when (state) { - NoDeviceState -> NoDeviceView() + NoDeviceState -> DeviceConnectingView() is WorkingState -> when (state.result) { is IdleResult, is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp) - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp) - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp) + is 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) } } - }.exhaustive + } } } } 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 31e5f47e..6cb09a35 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 @@ -31,10 +31,15 @@ package no.nordicsemi.android.cgms.viewmodel +import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile @@ -42,18 +47,25 @@ 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.view.* -import no.nordicsemi.android.navigation.* +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.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.android.utils.getDevice -import no.nordicsemi.ui.scanner.ScannerDestinationId +import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @HiltViewModel internal class CGMViewModel @Inject constructor( private val repository: CGMRepository, - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { @@ -82,24 +94,22 @@ internal class CGMViewModel @Inject constructor( is OnWorkingModeSelected -> onCommandReceived(event.workingMode) NavigateUp -> navigationManager.navigateUp() OpenLoggerEvent -> repository.openLogger() - }.exhaustive + } } private fun requestBluetoothDevice() { - navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(CGMS_SERVICE_UUID)) + navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(CGMS_SERVICE_UUID)) - navigationManager.recentResult.onEach { - if (it.destinationId == ScannerDestinationId) { - handleArgs(it) - } - }.launchIn(viewModelScope) + navigationManager.resultFrom(ScannerDestinationId) + .onEach { handleResult(it) } + .launchIn(viewModelScope) } - private fun handleArgs(args: DestinationResult) { - when (args) { - is CancelDestinationResult -> navigationManager.navigateUp() - is SuccessDestinationResult -> repository.launch(args.getDevice()) - }.exhaustive + private fun handleResult(result: NavigationResult) { + when (result) { + is NavigationResult.Cancelled -> navigationManager.navigateUp() + is NavigationResult.Success -> repository.launch(result.value) + } } private fun onCommandReceived(workingMode: CGMServiceCommand) { @@ -108,7 +118,7 @@ internal class CGMViewModel @Inject constructor( CGMServiceCommand.REQUEST_LAST_RECORD -> repository.requestLastRecord() CGMServiceCommand.REQUEST_FIRST_RECORD -> repository.requestFirstRecord() CGMServiceCommand.DISCONNECT -> disconnect() - }.exhaustive + } } private fun disconnect() { diff --git a/profile_csc/build.gradle.kts b/profile_csc/build.gradle.kts index 8f010e4c..3418458c 100644 --- a/profile_csc/build.gradle.kts +++ b/profile_csc/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) } @@ -41,6 +41,7 @@ android { dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(project(":lib_ui")) implementation(project(":lib_utils")) @@ -52,6 +53,7 @@ dependencies { implementation(libs.nordic.navigation) implementation(libs.nordic.uiscanner) + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) 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 index 2850fd7a..5e44daf9 100644 --- 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 @@ -42,7 +42,7 @@ 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.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.service.ConnectionObserverAdapter import java.util.* 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 326410f2..b51e2cd1 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,16 +37,16 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* 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.logger.NordicLogger -import no.nordicsemi.android.logger.NordicLoggerFactory import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject import javax.inject.Singleton @@ -72,7 +72,7 @@ class CSCRepository @Inject constructor( } fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CSC", device.address()).also { + val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CSC", device.address).also { logger = it } val manager = CSCManager(context, scope, createdLogger) @@ -103,7 +103,7 @@ class CSCRepository @Inject constructor( } fun openLogger() { - logger?.openLogger() + NordicLogger.launch(context, logger) } fun release() { 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 d601c449..7b9acf43 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 @@ -36,9 +36,9 @@ 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.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject @AndroidEntryPoint 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 8945706e..9640e83e 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 @@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Button +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf @@ -48,15 +49,13 @@ import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource 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.theme.RadioButtonGroup -import no.nordicsemi.android.theme.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle import no.nordicsemi.android.ui.view.dialog.FlowCanceled import no.nordicsemi.android.ui.view.dialog.ItemSelectedResult -import no.nordicsemi.android.utils.exhaustive @Composable internal fun CSCContentView(state: CSCData, speedUnit: SpeedUnit, onEvent: (CSCViewEvent) -> Unit) { @@ -74,7 +73,7 @@ internal fun CSCContentView(state: CSCData, speedUnit: SpeedUnit, onEvent: (CSCV wheelEntries[it.index]))) showDialog.value = false } - }.exhaustive + } } } @@ -105,7 +104,7 @@ private fun SettingsSection( onEvent: (CSCViewEvent) -> Unit, onWheelButtonClick: () -> Unit, ) { - ScreenSection { + OutlinedCard { Column( horizontalAlignment = Alignment.CenterHorizontally ) { 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 73000b1f..43589f94 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 @@ -31,9 +31,9 @@ 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.theme.RadioButtonItem -import no.nordicsemi.android.theme.RadioGroupViewEntity 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 dad0d33e..7cec7bee 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 @@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource 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.service.* +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.utils.exhaustive -import no.nordicsemi.ui.scanner.ui.DeviceConnectingView -import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView -import no.nordicsemi.ui.scanner.ui.NoDeviceView -import no.nordicsemi.ui.scanner.ui.Reason +import no.nordicsemi.android.ui.view.NavigateUpButton @Composable fun CSCScreen() { @@ -62,18 +69,18 @@ fun CSCScreen() { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { when (state.cscManagerState) { - NoDeviceState -> NoDeviceView() + NoDeviceState -> DeviceConnectingView() is WorkingState -> when (state.cscManagerState.result) { is IdleResult, is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(OnDisconnectButtonClick) } is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(OnDisconnectButtonClick) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp) - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp) - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp) - is SuccessResult -> CSCContentView(state.cscManagerState.result.data, state.speedUnit) { viewModel.onEvent(it) } + 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) } } - }.exhaustive + } } } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SelectWheelSizeDialog.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SelectWheelSizeDialog.kt index 87f58a72..1cb03414 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SelectWheelSizeDialog.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/SelectWheelSizeDialog.kt @@ -35,8 +35,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import no.nordicsemi.android.common.theme.NordicTheme import no.nordicsemi.android.csc.R -import no.nordicsemi.android.theme.NordicTheme import no.nordicsemi.android.ui.view.dialog.StringListDialog import no.nordicsemi.android.ui.view.dialog.StringListDialogConfig import no.nordicsemi.android.ui.view.dialog.StringListDialogResult 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 ea924d8d..78c90f35 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 @@ -34,6 +34,7 @@ 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.material3.OutlinedCard import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -41,14 +42,13 @@ 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.theme.ScreenSection import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.SectionTitle @Composable internal fun SensorsReadingView(state: CSCData, speedUnit: SpeedUnit) { - ScreenSection { + OutlinedCard { SectionTitle(resId = R.drawable.ic_records, title = "Records") Spacer(modifier = Modifier.height(16.dp)) 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 a4888752..d072426c 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 @@ -31,6 +31,7 @@ package no.nordicsemi.android.csc.viewmodel +import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -39,20 +40,20 @@ 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.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.view.* -import no.nordicsemi.android.navigation.* import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.android.utils.getDevice -import no.nordicsemi.ui.scanner.ScannerDestinationId +import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @HiltViewModel internal class CSCViewModel @Inject constructor( private val repository: CSCRepository, - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { @@ -76,20 +77,18 @@ internal class CSCViewModel @Inject constructor( } private fun requestBluetoothDevice() { - navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(CSC_SERVICE_UUID)) + navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(CSC_SERVICE_UUID)) - navigationManager.recentResult.onEach { - if (it.destinationId == ScannerDestinationId) { - handleArgs(it) - } - }.launchIn(viewModelScope) + navigationManager.resultFrom(ScannerDestinationId) + .onEach { handleResult(it) } + .launchIn(viewModelScope) } - private fun handleArgs(args: DestinationResult) { - when (args) { - is CancelDestinationResult -> navigationManager.navigateUp() - is SuccessDestinationResult -> repository.launch(args.getDevice()) - }.exhaustive + private fun handleResult(result: NavigationResult) { + when (result) { + is NavigationResult.Cancelled -> navigationManager.navigateUp() + is NavigationResult.Success -> repository.launch(result.value) + } } fun onEvent(event: CSCViewEvent) { @@ -99,7 +98,7 @@ internal class CSCViewModel @Inject constructor( OnDisconnectButtonClick -> disconnect() NavigateUp -> navigationManager.navigateUp() OpenLogger -> repository.openLogger() - }.exhaustive + } } private fun setSpeedUnit(speedUnit: SpeedUnit) { diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index cfd2ed32..5a2d4e70 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) } @@ -41,6 +41,7 @@ android { dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(project(":lib_ui")) implementation(project(":lib_utils")) @@ -53,6 +54,7 @@ dependencies { implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) 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 f98215d7..00e0ef92 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 @@ -31,13 +31,11 @@ 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.navigation.ComposeDestination -import no.nordicsemi.android.navigation.ComposeDestinations -import no.nordicsemi.android.navigation.DestinationId -internal val GlsDetailsDestinationId = DestinationId("gls-details-screen") +internal val GlsDetailsDestinationId = createDestination("gls-details-screen") -private val destination: ComposeDestination = ComposeDestination(GlsDetailsDestinationId) { GLSDetailsScreen() } - -val GLSDestinations = ComposeDestinations(destination) +val GLSDestination = defineDestination(GlsDetailsDestinationId) { GLSDetailsScreen() } 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 bc5a2b8f..ba51274a 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 @@ -47,7 +47,7 @@ import no.nordicsemi.android.ble.common.callback.glucose.GlucoseMeasurementRespo 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.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.service.ConnectionObserverAdapter import no.nordicsemi.android.utils.launchWithCatch import java.util.* 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 index a24cc88a..e2228660 100644 --- 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 @@ -39,8 +39,8 @@ internal data class GLSRecord( val time: Calendar? = null, val glucoseConcentration: Float = 0f, val unit: ConcentrationUnit = ConcentrationUnit.UNIT_KGPL, - val type: RecordType?, - val status: GlucoseStatus?, + val type: RecordType? = null, + val status: GlucoseStatus? = null, val sampleLocation: SampleLocation? = null, var context: MeasurementContext? = null ) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/Field.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/Field.kt index d001b2b4..12869a2c 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/Field.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/Field.kt @@ -84,7 +84,7 @@ internal fun BooleanField(title: String, value: Boolean) { Text( text = stringResource(id = R.string.gls_no), style = MaterialTheme.typography.bodyMedium, - color = colorResource(id = no.nordicsemi.android.theme.R.color.nordicGrass) + color = colorResource(id = R.color.nordicGrass) ) } } 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 f1e0b4ae..01e3ca04 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 @@ -36,6 +36,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Divider import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -45,13 +46,12 @@ 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.theme.ScreenSection @Composable internal fun GLSDetailsContentView(record: GLSRecord) { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { Column(modifier = Modifier.padding(16.dp)) { - ScreenSection() { + OutlinedCard { Field( stringResource(id = R.string.gls_details_sequence_number), record.sequenceNumber.toString() diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/viewmodel/GLSDetailsViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/viewmodel/GLSDetailsViewModel.kt index ca0c4b2f..1c5bd9ec 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/viewmodel/GLSDetailsViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/viewmodel/GLSDetailsViewModel.kt @@ -31,21 +31,23 @@ package no.nordicsemi.android.gls.details.viewmodel -import androidx.lifecycle.ViewModel +import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import no.nordicsemi.android.common.navigation.Navigator +import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewModel import no.nordicsemi.android.gls.GlsDetailsDestinationId -import no.nordicsemi.android.gls.data.GLSRecord -import no.nordicsemi.android.navigation.AnyArgument -import no.nordicsemi.android.navigation.NavigationManager import javax.inject.Inject @HiltViewModel internal class GLSDetailsViewModel @Inject constructor( - private val navigationManager: NavigationManager, -) : ViewModel() { + private val navigationManager: Navigator, + private val savedStateHandle: SavedStateHandle +) : SimpleNavigationViewModel(navigationManager, savedStateHandle) { - val record = - (navigationManager.getImmediateArgument(GlsDetailsDestinationId) as AnyArgument).value as GLSRecord + private val _record = MutableStateFlow(parameterOf(GlsDetailsDestinationId)) + val record = _record.asStateFlow() fun navigateBack() { navigationManager.navigateUp() 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 6b4006eb..64bf4fda 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 @@ -40,6 +40,7 @@ import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -54,7 +55,6 @@ 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.theme.ScreenSection import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.SectionTitle @@ -94,7 +94,7 @@ internal fun GLSContentView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Uni @Composable private fun SettingsView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Unit) { - ScreenSection { + OutlinedCard { SectionTitle(icon = Icons.Default.Settings, title = "Request items") Spacer(modifier = Modifier.height(16.dp)) @@ -118,7 +118,7 @@ private fun SettingsView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Unit) @Composable private fun RecordsView(state: GLSData) { - ScreenSection { + OutlinedCard { if (state.records.isEmpty()) { RecordsViewWithoutData() } else { 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 7742cac5..a9a3f9e7 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 @@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource 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.service.* +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.utils.exhaustive -import no.nordicsemi.ui.scanner.ui.DeviceConnectingView -import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView -import no.nordicsemi.ui.scanner.ui.NoDeviceView -import no.nordicsemi.ui.scanner.ui.Reason +import no.nordicsemi.android.ui.view.NavigateUpButton @Composable fun GLSScreen() { @@ -62,18 +69,18 @@ fun GLSScreen() { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { when (state) { - NoDeviceState -> NoDeviceView() + NoDeviceState -> DeviceConnectingView() is WorkingState -> when (state.result) { is IdleResult, is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp) - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp) - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp) + is 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) } } - }.exhaustive + } } } } 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 b73f5ddf..7a3b57fc 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,6 +31,7 @@ package no.nordicsemi.android.gls.main.viewmodel +import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -41,22 +42,21 @@ import kotlinx.coroutines.flow.onEach import no.nordicsemi.android.analytics.AppAnalytics 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.* import no.nordicsemi.android.gls.repository.GLSRepository -import no.nordicsemi.android.navigation.* import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.android.utils.getDevice -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice -import no.nordicsemi.ui.scanner.ScannerDestinationId +import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @HiltViewModel internal class GLSViewModel @Inject constructor( private val repository: GLSRepository, - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { @@ -64,20 +64,18 @@ internal class GLSViewModel @Inject constructor( val state = _state.asStateFlow() init { - navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(GLS_SERVICE_UUID)) + navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(GLS_SERVICE_UUID)) - navigationManager.recentResult.onEach { - if (it.destinationId == ScannerDestinationId) { - handleArgs(it) - } - }.launchIn(viewModelScope) + navigationManager.resultFrom(ScannerDestinationId) + .onEach { handleResult(it) } + .launchIn(viewModelScope) } - private fun handleArgs(args: DestinationResult) { - when (args) { - is CancelDestinationResult -> navigationManager.navigateUp() - is SuccessDestinationResult -> connectDevice(args.getDevice()) - }.exhaustive + private fun handleResult(result: NavigationResult) { + when (result) { + is NavigationResult.Cancelled -> navigationManager.navigateUp() + is NavigationResult.Success -> connectDevice(result.value) + } } fun onEvent(event: GLSScreenViewEvent) { @@ -85,9 +83,9 @@ internal class GLSViewModel @Inject constructor( OpenLoggerEvent -> repository.openLogger() DisconnectEvent -> navigationManager.navigateUp() is OnWorkingModeSelected -> repository.requestMode(event.workingMode) - is OnGLSRecordClick -> navigationManager.navigateTo(GlsDetailsDestinationId, AnyArgument(event.record)) + is OnGLSRecordClick -> navigationManager.navigateTo(GlsDetailsDestinationId, event.record) DisconnectEvent -> navigationManager.navigateUp() - }.exhaustive + } } private fun connectDevice(device: DiscoveredBluetoothDevice) { 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 3cd6072c..cbf5c576 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 @@ -42,15 +42,14 @@ 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.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.logger.NordicLogger -import no.nordicsemi.android.logger.NordicLoggerFactory import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject @ViewModelScoped @@ -65,7 +64,7 @@ internal class GLSRepository @Inject constructor( private var logger: NordicLogger? = null fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow> = callbackFlow { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "GLS", device.address()).also { + val createdLogger = loggerFactory.create(stringConst.APP_NAME, "GLS", device.address).also { logger = it } val managerInstance = manager ?: GLSManager(context, scope, createdLogger) @@ -100,7 +99,7 @@ internal class GLSRepository @Inject constructor( } fun openLogger() { - logger?.openLogger() + NordicLogger.launch(context, logger) } fun requestMode(workingMode: WorkingMode) { @@ -108,6 +107,6 @@ internal class GLSRepository @Inject constructor( WorkingMode.ALL -> manager?.requestAllRecords() WorkingMode.LAST -> manager?.requestLastRecord() WorkingMode.FIRST -> manager?.requestFirstRecord() - }.exhaustive + } } } diff --git a/profile_hrs/build.gradle.kts b/profile_hrs/build.gradle.kts index 0af85b14..5ebcb6f0 100644 --- a/profile_hrs/build.gradle.kts +++ b/profile_hrs/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) } @@ -41,6 +41,7 @@ android { dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(project(":lib_ui")) implementation(project(":lib_utils")) @@ -53,6 +54,7 @@ dependencies { implementation(libs.nordic.uiscanner) implementation(libs.nordic.uilogger) + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) 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 index ce759ed7..d02ab5cc 100644 --- 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 @@ -44,7 +44,7 @@ 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.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.service.ConnectionObserverAdapter import no.nordicsemi.android.utils.launchWithCatch import java.util.* 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 d9dc4287..b12efe66 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 @@ -34,18 +34,22 @@ package no.nordicsemi.android.hrs.service import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* +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.hrs.data.HRSData import no.nordicsemi.android.hrs.data.HRSManager -import no.nordicsemi.android.logger.NordicLogger -import no.nordicsemi.android.logger.NordicLoggerFactory import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject import javax.inject.Singleton @@ -71,7 +75,7 @@ class HRSRepository @Inject constructor( } fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HRS", device.address()).also { + val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HRS", device.address).also { logger = it } val manager = HRSManager(context, scope, createdLogger) @@ -87,7 +91,7 @@ class HRSRepository @Inject constructor( } fun openLogger() { - logger?.openLogger() + NordicLogger.launch(context, logger) } private suspend fun HRSManager.start(device: DiscoveredBluetoothDevice) { 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 0b490a70..fce7e486 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,9 +36,9 @@ 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.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject @AndroidEntryPoint 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 239e54d8..6948aa10 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 @@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -48,7 +49,6 @@ 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.theme.ScreenSection import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.SectionTitle @@ -59,7 +59,7 @@ internal fun HRSContentView(state: HRSData, zoomIn: Boolean, onEvent: (HRSScreen modifier = Modifier.padding(16.dp) ) { - ScreenSection { + OutlinedCard { SectionTitle( resId = R.drawable.ic_chart_line, title = stringResource(id = R.string.hrs_section_data), 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 53d0a7d3..328a2d41 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 @@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource 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.service.* +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.utils.exhaustive -import no.nordicsemi.ui.scanner.ui.DeviceConnectingView -import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView -import no.nordicsemi.ui.scanner.ui.NoDeviceView -import no.nordicsemi.ui.scanner.ui.Reason +import no.nordicsemi.android.ui.view.NavigateUpButton @Composable fun HRSScreen() { @@ -62,18 +69,18 @@ fun HRSScreen() { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { when (state) { - NoDeviceState -> NoDeviceView() + NoDeviceState -> DeviceConnectingView() is WorkingState -> when (state.result) { is IdleResult, is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp) - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp) - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp) + is 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) } } - }.exhaustive + } } } } 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 b381370f..5f276cd6 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 @@ -31,28 +31,40 @@ package no.nordicsemi.android.hrs.viewmodel +import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.launchIn +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.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.* -import no.nordicsemi.android.navigation.* +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.service.ConnectedResult -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.android.utils.getDevice -import no.nordicsemi.ui.scanner.ScannerDestinationId +import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @HiltViewModel internal class HRSViewModel @Inject constructor( private val repository: HRSRepository, - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { @@ -77,20 +89,18 @@ internal class HRSViewModel @Inject constructor( } private fun requestBluetoothDevice() { - navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(HRS_SERVICE_UUID)) + navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(HRS_SERVICE_UUID)) - navigationManager.recentResult.onEach { - if (it.destinationId == ScannerDestinationId) { - handleArgs(it) - } - }.launchIn(viewModelScope) + navigationManager.resultFrom(ScannerDestinationId) + .onEach { handleResult(it) } + .launchIn(viewModelScope) } - private fun handleArgs(args: DestinationResult) { - when (args) { - is CancelDestinationResult -> navigationManager.navigateUp() - is SuccessDestinationResult -> repository.launch(args.getDevice()) - }.exhaustive + private fun handleResult(result: NavigationResult) { + when (result) { + is NavigationResult.Cancelled -> navigationManager.navigateUp() + is NavigationResult.Success -> repository.launch(result.value) + } } fun onEvent(event: HRSScreenViewEvent) { @@ -99,7 +109,7 @@ internal class HRSViewModel @Inject constructor( NavigateUpEvent -> navigationManager.navigateUp() OpenLoggerEvent -> repository.openLogger() SwitchZoomEvent -> onZoomButtonClicked() - }.exhaustive + } } private fun onZoomButtonClicked() { diff --git a/profile_hts/build.gradle.kts b/profile_hts/build.gradle.kts index acdcef3c..7bafc1e2 100644 --- a/profile_hts/build.gradle.kts +++ b/profile_hts/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) } @@ -41,6 +41,7 @@ android { dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(project(":lib_ui")) implementation(project(":lib_utils")) @@ -52,6 +53,7 @@ dependencies { implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) 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 index 2bccb2dd..5d4e8cb2 100644 --- 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 @@ -42,7 +42,7 @@ 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.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.service.ConnectionObserverAdapter import java.util.* 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 ba1fb4ca..16e1f9f3 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 @@ -34,18 +34,22 @@ package no.nordicsemi.android.hts.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* +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.hts.data.HTSData import no.nordicsemi.android.hts.data.HTSManager -import no.nordicsemi.android.logger.NordicLogger -import no.nordicsemi.android.logger.NordicLoggerFactory import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject import javax.inject.Singleton @@ -71,7 +75,7 @@ class HTSRepository @Inject constructor( } fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HTS", device.address()).also { + val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HTS", device.address).also { logger = it } val manager = HTSManager(context, scope, createdLogger) @@ -87,7 +91,7 @@ class HTSRepository @Inject constructor( } fun openLogger() { - logger?.openLogger() + NordicLogger.launch(context, logger) } private suspend fun HTSManager.start(device: DiscoveredBluetoothDevice) { 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 24bc151f..50bb962e 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,9 +36,9 @@ 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.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject @AndroidEntryPoint 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 df509029..e4fa1e94 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 @@ -33,6 +33,7 @@ package no.nordicsemi.android.hts.view import androidx.compose.foundation.layout.* import androidx.compose.material3.Button +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -40,10 +41,9 @@ 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.common.theme.view.RadioButtonGroup import no.nordicsemi.android.hts.R import no.nordicsemi.android.hts.data.HTSData -import no.nordicsemi.android.theme.RadioButtonGroup -import no.nordicsemi.android.theme.ScreenSection import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.SectionTitle @@ -56,7 +56,7 @@ internal fun HTSContentView(state: HTSData, temperatureUnit: TemperatureUnit, on .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - ScreenSection { + OutlinedCard { SectionTitle(resId = R.drawable.ic_thermometer, title = "Settings") Spacer(modifier = Modifier.height(16.dp)) @@ -68,7 +68,7 @@ internal fun HTSContentView(state: HTSData, temperatureUnit: TemperatureUnit, on Spacer(modifier = Modifier.height(16.dp)) - ScreenSection { + OutlinedCard { SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.hts_records_section)) Spacer(modifier = Modifier.height(16.dp)) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSMapper.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSMapper.kt index 7bd11219..7b1ebc0f 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSMapper.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSMapper.kt @@ -31,8 +31,8 @@ package no.nordicsemi.android.hts.view -import no.nordicsemi.android.theme.RadioButtonItem -import no.nordicsemi.android.theme.RadioGroupViewEntity +import no.nordicsemi.android.common.theme.view.RadioButtonItem +import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity private const val DISPLAY_FAHRENHEIT = "°F" private const val DISPLAY_CELSIUS = "°C" 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 9cda41b4..ae7ac65d 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 @@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource 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.service.* +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.utils.exhaustive -import no.nordicsemi.ui.scanner.ui.DeviceConnectingView -import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView -import no.nordicsemi.ui.scanner.ui.NoDeviceView -import no.nordicsemi.ui.scanner.ui.Reason +import no.nordicsemi.android.ui.view.NavigateUpButton @Composable fun HTSScreen() { @@ -62,18 +69,18 @@ fun HTSScreen() { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { when (state.htsManagerState) { - NoDeviceState -> NoDeviceView() + NoDeviceState -> DeviceConnectingView() is WorkingState -> when (state.htsManagerState.result) { is IdleResult, is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp) - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp) - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp) + is 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) } } - }.exhaustive + } } } } 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 652faadc..1ffb4bd4 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 @@ -31,28 +31,39 @@ package no.nordicsemi.android.hts.viewmodel +import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.launchIn +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.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.* -import no.nordicsemi.android.navigation.* +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.service.ConnectedResult -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.android.utils.getDevice -import no.nordicsemi.ui.scanner.ScannerDestinationId +import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @HiltViewModel internal class HTSViewModel @Inject constructor( private val repository: HTSRepository, - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { @@ -76,20 +87,18 @@ internal class HTSViewModel @Inject constructor( } private fun requestBluetoothDevice() { - navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(HTS_SERVICE_UUID)) + navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(HTS_SERVICE_UUID)) - navigationManager.recentResult.onEach { - if (it.destinationId == ScannerDestinationId) { - handleArgs(it) - } - }.launchIn(viewModelScope) + navigationManager.resultFrom(ScannerDestinationId) + .onEach { handleResult(it) } + .launchIn(viewModelScope) } - private fun handleArgs(args: DestinationResult) { - when (args) { - is CancelDestinationResult -> navigationManager.navigateUp() - is SuccessDestinationResult -> repository.launch(args.getDevice()) - }.exhaustive + private fun handleResult(result: NavigationResult) { + when (result) { + is NavigationResult.Cancelled -> navigationManager.navigateUp() + is NavigationResult.Success -> repository.launch(result.value) + } } fun onEvent(event: HTSScreenViewEvent) { @@ -98,7 +107,7 @@ internal class HTSViewModel @Inject constructor( is OnTemperatureUnitSelected -> onTemperatureUnitSelected(event) NavigateUp -> navigationManager.navigateUp() OpenLoggerEvent -> repository.openLogger() - }.exhaustive + } } private fun disconnect() { diff --git a/profile_prx/build.gradle.kts b/profile_prx/build.gradle.kts index 245f6496..f8585a6f 100644 --- a/profile_prx/build.gradle.kts +++ b/profile_prx/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) } @@ -41,6 +41,7 @@ android { dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(project(":lib_ui")) implementation(project(":lib_utils")) @@ -52,6 +53,7 @@ dependencies { implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) 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 85dd4d1e..292b2bf8 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 @@ -46,7 +46,7 @@ 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.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicLogger 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/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 13cc22ea..82ef910e 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 @@ -35,15 +35,15 @@ import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* -import no.nordicsemi.android.logger.NordicLogger -import no.nordicsemi.android.logger.NordicLoggerFactory +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.prx.data.AlarmLevel import no.nordicsemi.android.prx.data.PRXData import no.nordicsemi.android.prx.data.PRXManager import no.nordicsemi.android.prx.data.ProximityServerManager import no.nordicsemi.android.service.* import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject import javax.inject.Singleton @@ -73,7 +73,7 @@ class PRXRepository @Inject internal constructor( } fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "PRX", device.address()).also { + val createdLogger = loggerFactory.create(stringConst.APP_NAME, "PRX", device.address).also { logger = it } val manager = PRXManager(context, scope, createdLogger) @@ -114,7 +114,7 @@ class PRXRepository @Inject internal constructor( } fun openLogger() { - logger?.openLogger() + NordicLogger.launch(context, logger) } 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 b310f51c..1b19f49e 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,9 +36,9 @@ 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.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject @AndroidEntryPoint 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 192d4cb2..6b99dbd2 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 @@ -38,13 +38,13 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Button +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import no.nordicsemi.android.theme.ScreenSection import no.nordicsemi.android.prx.R import no.nordicsemi.android.prx.data.PRXData import no.nordicsemi.android.ui.view.BatteryLevelView @@ -81,7 +81,7 @@ internal fun ContentView(state: PRXData, onEvent: (PRXScreenViewEvent) -> Unit) @Composable private fun SettingsSection(state: PRXData, onEvent: (PRXScreenViewEvent) -> Unit) { - ScreenSection { + OutlinedCard { SectionTitle(icon = Icons.Default.Settings, title = stringResource(R.string.prx_settings)) Spacer(modifier = Modifier.height(16.dp)) @@ -114,7 +114,7 @@ private fun TurnAlarmOffButton(onEvent: (PRXScreenViewEvent) -> Unit) { @Composable private fun RecordsSection(state: PRXData) { - ScreenSection { + OutlinedCard { SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.prx_records)) Spacer(modifier = Modifier.height(16.dp)) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXLinkLossView.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXLinkLossView.kt index 0cbe7be4..2545ed20 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXLinkLossView.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/view/PRXLinkLossView.kt @@ -39,6 +39,7 @@ import androidx.compose.material.icons.filled.HighlightOff import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -46,7 +47,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import no.nordicsemi.android.theme.ScreenSection import no.nordicsemi.android.prx.R @Composable @@ -57,7 +57,7 @@ fun DeviceOutOfRangeView(navigateUp: () -> Unit) { .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - ScreenSection { + OutlinedCard { Icon( imageVector = Icons.Default.HighlightOff, contentDescription = null, 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 0a7e4900..5a41267b 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 @@ -40,16 +40,23 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource 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.prx.R import no.nordicsemi.android.prx.viewmodel.PRXViewModel -import no.nordicsemi.android.service.* +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.utils.exhaustive -import no.nordicsemi.ui.scanner.ui.DeviceConnectingView -import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView -import no.nordicsemi.ui.scanner.ui.NoDeviceView -import no.nordicsemi.ui.scanner.ui.Reason +import no.nordicsemi.android.ui.view.NavigateUpButton @Composable fun PRXScreen() { @@ -63,18 +70,18 @@ fun PRXScreen() { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { when (state) { - NoDeviceState -> NoDeviceView() + NoDeviceState -> DeviceConnectingView() is WorkingState -> when (state.result) { is IdleResult, is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp) - is LinkLossResult -> DeviceOutOfRangeView { viewModel.onEvent(DisconnectEvent) } - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, 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) } } - }.exhaustive + } } } } 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 779a56c0..37fe9a69 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 @@ -31,6 +31,7 @@ package no.nordicsemi.android.prx.viewmodel +import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -39,20 +40,20 @@ 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.navigation.* +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.prx.data.PRX_SERVICE_UUID import no.nordicsemi.android.prx.repository.PRXRepository import no.nordicsemi.android.prx.view.* import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.android.utils.getDevice -import no.nordicsemi.ui.scanner.ScannerDestinationId +import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @HiltViewModel internal class PRXViewModel @Inject constructor( private val repository: PRXRepository, - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { @@ -76,20 +77,18 @@ internal class PRXViewModel @Inject constructor( } private fun requestBluetoothDevice() { - navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(PRX_SERVICE_UUID)) + navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(PRX_SERVICE_UUID)) - navigationManager.recentResult.onEach { - if (it.destinationId == ScannerDestinationId) { - handleArgs(it) - } - }.launchIn(viewModelScope) + navigationManager.resultFrom(ScannerDestinationId) + .onEach { handleResult(it) } + .launchIn(viewModelScope) } - private fun handleArgs(args: DestinationResult) { - when (args) { - is CancelDestinationResult -> navigationManager.navigateUp() - is SuccessDestinationResult -> repository.launch(args.getDevice()) - }.exhaustive + private fun handleResult(result: NavigationResult) { + when (result) { + is NavigationResult.Cancelled -> navigationManager.navigateUp() + is NavigationResult.Success -> repository.launch(result.value) + } } fun onEvent(event: PRXScreenViewEvent) { @@ -99,7 +98,7 @@ internal class PRXViewModel @Inject constructor( TurnOnAlert -> repository.enableAlarm() NavigateUpEvent -> navigationManager.navigateUp() OpenLoggerEvent -> repository.openLogger() - }.exhaustive + } } private fun disconnect() { diff --git a/profile_rscs/build.gradle.kts b/profile_rscs/build.gradle.kts index 6b409008..92d50647 100644 --- a/profile_rscs/build.gradle.kts +++ b/profile_rscs/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) } @@ -41,6 +41,7 @@ android { dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(project(":lib_ui")) implementation(project(":lib_utils")) @@ -52,6 +53,7 @@ dependencies { implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) 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 index 14ee68db..30668a64 100644 --- 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 @@ -42,7 +42,7 @@ 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.logger.NordicLogger +import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.service.ConnectionObserverAdapter import java.util.* 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 3f4dc899..fe7af444 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 @@ -34,18 +34,22 @@ package no.nordicsemi.android.rscs.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* +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.logger.NordicLogger -import no.nordicsemi.android.logger.NordicLoggerFactory +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.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.service.ServiceManager import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject import javax.inject.Singleton @@ -71,7 +75,7 @@ class RSCSRepository @Inject constructor( } fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "RSCS", device.address()).also { + val createdLogger = loggerFactory.create(stringConst.APP_NAME, "RSCS", device.address).also { logger = it } val manager = RSCSManager(context, scope, createdLogger) @@ -87,7 +91,7 @@ class RSCSRepository @Inject constructor( } fun openLogger() { - logger?.openLogger() + NordicLogger.launch(context, logger) } private suspend fun RSCSManager.start(device: DiscoveredBluetoothDevice) { 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 78f23320..c9681d4a 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 @@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource 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.rscs.R import no.nordicsemi.android.rscs.viewmodel.RSCSViewModel -import no.nordicsemi.android.service.* +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.utils.exhaustive -import no.nordicsemi.ui.scanner.ui.DeviceConnectingView -import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView -import no.nordicsemi.ui.scanner.ui.NoDeviceView -import no.nordicsemi.ui.scanner.ui.Reason +import no.nordicsemi.android.ui.view.NavigateUpButton @Composable fun RSCSScreen() { @@ -62,18 +69,18 @@ fun RSCSScreen() { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { when (state) { - NoDeviceState -> NoDeviceView() + NoDeviceState -> DeviceConnectingView() is WorkingState -> when (state.result) { is IdleResult, is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp) - is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp) - is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) - is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp) + is 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) } } - }.exhaustive + } } } } 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 356be288..0d0b33ec 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,12 +33,12 @@ 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 import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import no.nordicsemi.android.theme.ScreenSection import no.nordicsemi.android.rscs.R import no.nordicsemi.android.rscs.data.RSCSData import no.nordicsemi.android.ui.view.KeyValueField @@ -46,7 +46,7 @@ import no.nordicsemi.android.ui.view.SectionTitle @Composable internal fun SensorsReadingView(state: RSCSData) { - ScreenSection { + OutlinedCard { SectionTitle(resId = R.drawable.ic_records, title = "Records") Spacer(modifier = Modifier.height(16.dp)) 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 a8e8b3f4..2dbbe12a 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 @@ -31,28 +31,39 @@ package no.nordicsemi.android.rscs.viewmodel +import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.launchIn +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.navigation.* +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.rscs.data.RSCS_SERVICE_UUID import no.nordicsemi.android.rscs.repository.RSCSRepository -import no.nordicsemi.android.rscs.view.* +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.utils.exhaustive -import no.nordicsemi.android.utils.getDevice -import no.nordicsemi.ui.scanner.ScannerDestinationId +import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @HiltViewModel internal class RSCSViewModel @Inject constructor( private val repository: RSCSRepository, - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val analytics: AppAnalytics ) : ViewModel() { @@ -76,20 +87,18 @@ internal class RSCSViewModel @Inject constructor( } private fun requestBluetoothDevice() { - navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(RSCS_SERVICE_UUID)) + navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(RSCS_SERVICE_UUID)) - navigationManager.recentResult.onEach { - if (it.destinationId == ScannerDestinationId) { - handleArgs(it) - } - }.launchIn(viewModelScope) + navigationManager.resultFrom(ScannerDestinationId) + .onEach { handleResult(it) } + .launchIn(viewModelScope) } - private fun handleArgs(args: DestinationResult) { - when (args) { - is CancelDestinationResult -> navigationManager.navigateUp() - is SuccessDestinationResult -> repository.launch(args.getDevice()) - }.exhaustive + private fun handleResult(result: NavigationResult) { + when (result) { + is NavigationResult.Cancelled -> navigationManager.navigateUp() + is NavigationResult.Success -> repository.launch(result.value) + } } fun onEvent(event: RSCScreenViewEvent) { @@ -97,7 +106,7 @@ internal class RSCSViewModel @Inject constructor( DisconnectEvent -> disconnect() NavigateUpEvent -> navigationManager.navigateUp() OpenLoggerEvent -> repository.openLogger() - }.exhaustive + } } private fun disconnect() { diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index d8304e4a..ce2c1552 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -30,7 +30,7 @@ */ plugins { - alias(libs.plugins.nordic.library) + alias(libs.plugins.nordic.feature) alias(libs.plugins.kotlin.serialization) // id("com.google.protobuf") alias(libs.plugins.kotlin.kapt) @@ -49,6 +49,7 @@ android { dependencies { implementation(project(":lib_analytics")) implementation(project(":lib_service")) + implementation(project(":lib_scanner")) implementation(project(":lib_ui")) implementation(project(":lib_utils")) @@ -56,7 +57,7 @@ dependencies { implementation(libs.room.ktx) // kapt(libs.room.compiler) -// kapt("androidx.room:room-compiler:2.5.0") + kapt(libs.room.compiler) // kapt("") implementation(libs.nordic.ble.common) @@ -67,6 +68,10 @@ dependencies { implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.androidx.dataStore.core) + implementation(libs.androidx.dataStore.preferences) + + implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) 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 index 8855b76c..fe0c165f 100644 --- 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 @@ -45,7 +45,7 @@ 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.logger.NordicLogger +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 diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.java b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt similarity index 56% rename from profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.java rename to profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt index 1691252e..c8742848 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.java +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt @@ -28,57 +28,41 @@ * 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.db -package no.nordicsemi.android.uart.db; - -import org.simpleframework.xml.Attribute; -import org.simpleframework.xml.ElementArray; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.core.PersistenceException; -import org.simpleframework.xml.core.Validate; +import org.simpleframework.xml.Attribute +import org.simpleframework.xml.ElementArray +import org.simpleframework.xml.Root +import org.simpleframework.xml.core.PersistenceException +import org.simpleframework.xml.core.Validate @Root -public class XmlConfiguration { - public static final int COMMANDS_COUNT = 9; +class XmlConfiguration { + /** + * Returns the field name + * + * @return optional name + */ + /** + * Sets the name to specified value + * @param name the new name + */ + @field:Attribute(required = false, empty = "Unnamed") + var name: String? = null - @Attribute(required = false, empty = "Unnamed") - private String name; + /** + * Returns the array of commands. There is always 9 of them. + * @return the commands array + */ + @field:ElementArray + var commands: Array = arrayOfNulls(COMMANDS_COUNT) + @Validate + @Throws(PersistenceException::class) + private fun validate() { + if (commands.size != COMMANDS_COUNT) throw PersistenceException("There must be always $COMMANDS_COUNT commands in a configuration.") + } - @ElementArray - private XmlMacro[] commands = new XmlMacro[COMMANDS_COUNT]; - - /** - * Returns the field name - * - * @return optional name - */ - public String getName() { - return name; - } - - /** - * Sets the name to specified value - * @param name the new name - */ - public void setName(final String name) { - this.name = name; - } - - /** - * Returns the array of commands. There is always 9 of them. - * @return the commands array - */ - public XmlMacro[] getCommands() { - return commands; - } - - public void setCommands(XmlMacro[] commands) { - this.commands = commands; - } - - @Validate - private void validate() throws PersistenceException{ - if (commands == null || commands.length != COMMANDS_COUNT) - throw new PersistenceException("There must be always " + COMMANDS_COUNT + " commands in a configuration."); - } -} + companion object { + const val COMMANDS_COUNT = 9 + } +} \ No newline at end of file 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 deleted file mode 100644 index 202097bd..00000000 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.java +++ /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.uart.db; - -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.*; - -@Root -public class XmlMacro { - - @Text(required = false) - private String command; - - @Attribute(required = false) - private boolean active = false; - - @Attribute(required = false) - private MacroEol eol = MacroEol.LF; - - @Attribute(required = false) - private MacroIcon icon = MacroIcon.LEFT; - - /** - * Sets the command. - * @param command the command that will be sent to UART device - */ - public void setCommand(final String command) { - this.command = command; - } - - /** - * Sets whether the command is active. - * @param active true to make it active - */ - public void setActive(final boolean active) { - this.active = active; - } - - /** - * Sets the new line type. - * @param eol end of line terminator - */ - public void setEol(final int eol) { - this.eol = MacroEol.values()[eol]; - } - - /** - * Sets the icon index. - * @param index index of the icon. - */ - public void setIconIndex(final int index) { - this.icon = MacroIcon.values()[index]; - } - - /** - * Returns the command that will be sent to UART device. - * @return the command - */ - public String getCommand() { - return command; - } - - /** - * Returns whether the icon is active. - * @return true if it's active - */ - public boolean isActive() { - return active; - } - - /** - * Returns the new line type. - * @return end of line terminator - */ - public MacroEol getEol() { - return eol; - } - - /** - * Returns the icon index. - * @return the icon index - */ - public int getIconIndex() { - return icon.getIndex(); - } - /** - * Returns the EOL index. - * @return the EOL index - */ - public int getEolIndex() { - return eol.getIndex(); - } -} diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.kt new file mode 100644 index 00000000..010c807c --- /dev/null +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.kt @@ -0,0 +1,102 @@ +/* + * 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.db + +import no.nordicsemi.android.uart.data.MacroEol +import no.nordicsemi.android.uart.data.MacroIcon +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; +@Root +class XmlMacro { + /** + * Returns the command that will be sent to UART device. + * @return the command + */ + /** + * Sets the command. + * @param command the command that will be sent to UART device + */ + @field:Text(required = false) + var command: String? = null + /** + * Returns whether the icon is active. + * @return true if it's active + */ + /** + * Sets whether the command is active. + * @param active true to make it active + */ + @field:Attribute(required = false) + var isActive = false + + /** + * Returns the new line type. + * @return end of line terminator + */ + @field:Attribute(required = false) + var eol = MacroEol.LF + private set + + @field:Attribute(required = false) + private var icon = MacroIcon.LEFT + + /** + * Sets the new line type. + * @param eol end of line terminator + */ + fun setEol(eol: Int) { + this.eol = MacroEol.values()[eol] + } + /** + * Returns the icon index. + * @return the icon index + */ + /** + * Sets the icon index. + * @param index index of the icon. + */ + var iconIndex: Int + get() = icon.index + set(index) { + icon = MacroIcon.values()[index] + } + + /** + * Returns the EOL index. + * @return the EOL index + */ + val eolIndex: Int + get() = eol.index +} \ No newline at end of file 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 1c00da30..a01b1bcf 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 @@ -34,18 +34,27 @@ package no.nordicsemi.android.uart.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* +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.logger.NordicLogger -import no.nordicsemi.android.logger.NordicLoggerFactory +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.service.BleManagerResult import no.nordicsemi.android.service.IdleResult 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.parseWithNewLineChar import no.nordicsemi.android.ui.view.StringConst -import no.nordicsemi.android.uart.data.* import no.nordicsemi.android.utils.EMPTY -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject import javax.inject.Singleton @@ -74,7 +83,7 @@ class UARTRepository @Inject internal constructor( } fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "UART", device.address()).also { + val createdLogger = loggerFactory.create(stringConst.APP_NAME, "UART", device.address).also { logger = it } val manager = UARTManager(context, scope, createdLogger) @@ -103,7 +112,7 @@ class UARTRepository @Inject internal constructor( } fun openLogger() { - logger?.openLogger() + NordicLogger.launch(context, logger) } suspend fun saveConfigurationName(name: String) { 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 1222be9f..da4e4283 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,9 +36,9 @@ 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.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice import javax.inject.Inject @AndroidEntryPoint diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/InputSection.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/InputSection.kt index cc3d9453..7080ef97 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/InputSection.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/InputSection.kt @@ -45,15 +45,15 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch -import no.nordicsemi.android.theme.RadioButtonGroup -import no.nordicsemi.android.theme.RadioButtonItem -import no.nordicsemi.android.theme.RadioGroupViewEntity -import no.nordicsemi.android.theme.ScreenSection +import no.nordicsemi.android.common.theme.view.RadioButtonGroup +import no.nordicsemi.android.common.theme.view.RadioButtonItem +import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity import no.nordicsemi.android.ui.view.SectionTitle import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.data.MacroEol import no.nordicsemi.android.utils.EMPTY +@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun InputSection(onEvent: (UARTViewEvent) -> Unit) { val text = rememberSaveable { mutableStateOf(String.EMPTY) } @@ -105,7 +105,7 @@ internal fun EditInputSection(onEvent: (UARTViewEvent) -> Unit) { } val viewEntity = RadioGroupViewEntity(items) - ScreenSection { + OutlinedCard { Column( horizontalAlignment = Alignment.CenterHorizontally ) { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/MacroSection.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/MacroSection.kt index fd5c3e28..5bd0a8cb 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/MacroSection.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/MacroSection.kt @@ -46,7 +46,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import no.nordicsemi.android.theme.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle import no.nordicsemi.android.uart.R @@ -74,7 +73,7 @@ internal fun MacroSection(viewState: UARTViewState, onEvent: (UARTViewEvent) -> .verticalScroll(rememberScrollState()) .padding(16.dp) ) { - ScreenSection { + OutlinedCard { Column( horizontalAlignment = Alignment.CenterHorizontally ) { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddConfigurationDialog.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddConfigurationDialog.kt index 08fea8e0..0d3258ba 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddConfigurationDialog.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddConfigurationDialog.kt @@ -70,6 +70,7 @@ internal fun UARTAddConfigurationDialog(onEvent: (UARTViewEvent) -> Unit, onDism ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun NameInput( name: MutableState, diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt index 3dbe39db..d90ce04d 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt @@ -52,9 +52,9 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import no.nordicsemi.android.theme.RadioButtonGroup -import no.nordicsemi.android.theme.RadioButtonItem -import no.nordicsemi.android.theme.RadioGroupViewEntity +import no.nordicsemi.android.common.theme.view.RadioButtonGroup +import no.nordicsemi.android.common.theme.view.RadioButtonItem +import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.data.MacroEol import no.nordicsemi.android.uart.data.MacroIcon @@ -130,6 +130,7 @@ internal fun UARTAddMacroDialog(macro: UARTMacro?, onEvent: (UARTViewEvent) -> U ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun CommandInput(command: MutableState) { Column { diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTConfigurationPicker.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTConfigurationPicker.kt index 3645f52e..586d6b76 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTConfigurationPicker.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTConfigurationPicker.kt @@ -50,7 +50,6 @@ import androidx.compose.ui.res.stringResource import no.nordicsemi.android.ui.view.dialog.* import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.data.UARTConfiguration -import no.nordicsemi.android.utils.exhaustive @Composable internal fun UARTConfigurationPicker(state: UARTViewState, onEvent: (UARTViewEvent) -> Unit) { @@ -68,7 +67,7 @@ internal fun UARTConfigurationPicker(state: UARTViewState, onEvent: (UARTViewEve onEvent(OnConfigurationSelected(state.configurations[it.index])) showDialog.value = false } - }.exhaustive + } } } } 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 9b2d9b27..61087f04 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 @@ -32,11 +32,11 @@ package no.nordicsemi.android.uart.view import androidx.compose.foundation.layout.* +import androidx.compose.material3.Card 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.theme.Card import no.nordicsemi.android.uart.data.UARTData @Composable 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 d380b2b6..df1da97c 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 @@ -39,20 +39,27 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.theme.PagerView -import no.nordicsemi.android.theme.PagerViewEntity -import no.nordicsemi.android.theme.PagerViewItem -import no.nordicsemi.android.service.* -import no.nordicsemi.android.ui.view.BackIconAppBar -import no.nordicsemi.android.ui.view.LoggerIconAppBar +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.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.uart.R import no.nordicsemi.android.uart.data.UARTData import no.nordicsemi.android.uart.viewmodel.UARTViewModel -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.ui.scanner.ui.DeviceConnectingView -import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView -import no.nordicsemi.ui.scanner.ui.NoDeviceView -import no.nordicsemi.ui.scanner.ui.Reason +import no.nordicsemi.android.ui.view.BackIconAppBar +import no.nordicsemi.android.ui.view.LoggerIconAppBar +import no.nordicsemi.android.ui.view.NavigateUpButton @Composable fun UARTScreen() { @@ -64,19 +71,21 @@ fun UARTScreen() { AppBar(state = state, navigateUp = navigateUp) { viewModel.onEvent(it) } - when (state.uartManagerState) { - NoDeviceState -> NoDeviceView() - is WorkingState -> when (state.uartManagerState.result) { - is IdleResult, - is ConnectingResult -> Scroll { DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } } - is ConnectedResult -> Scroll { DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } } - is DisconnectedResult -> Scroll { DeviceDisconnectedView(Reason.USER, navigateUp) } - is LinkLossResult -> Scroll { DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp) } - is MissingServiceResult -> Scroll { DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) } - is UnknownErrorResult -> Scroll { DeviceDisconnectedView(Reason.UNKNOWN, navigateUp) } - is SuccessResult -> SuccessScreen(state.uartManagerState.result.data, state, viewModel) + Column(modifier = Modifier.verticalScroll(rememberScrollState())) { + when (state.uartManagerState) { + NoDeviceState -> DeviceConnectingView() + is WorkingState -> when (state.uartManagerState.result) { + is IdleResult, + is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } + is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } + 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 -> SuccessScreen(state.uartManagerState.result.data, state, viewModel) + } } - }.exhaustive + } } } 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 543da56a..7d736e37 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 @@ -31,28 +31,60 @@ package no.nordicsemi.android.uart.viewmodel +import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.* +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 import kotlinx.coroutines.launch -import no.nordicsemi.android.analytics.* -import no.nordicsemi.android.navigation.* +import no.nordicsemi.android.analytics.AppAnalytics +import no.nordicsemi.android.analytics.Profile +import no.nordicsemi.android.analytics.ProfileConnectedEvent +import no.nordicsemi.android.analytics.UARTChangeConfiguration +import no.nordicsemi.android.analytics.UARTCreateConfiguration +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.service.ConnectedResult import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.uart.data.* +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.view.* -import no.nordicsemi.android.utils.exhaustive -import no.nordicsemi.android.utils.getDevice -import no.nordicsemi.ui.scanner.ScannerDestinationId +import no.nordicsemi.android.uart.view.ClearOutputItems +import no.nordicsemi.android.uart.view.DisconnectEvent +import no.nordicsemi.android.uart.view.MacroInputSwitchClick +import no.nordicsemi.android.uart.view.NavigateUp +import no.nordicsemi.android.uart.view.OnAddConfiguration +import no.nordicsemi.android.uart.view.OnConfigurationSelected +import no.nordicsemi.android.uart.view.OnCreateMacro +import no.nordicsemi.android.uart.view.OnDeleteConfiguration +import no.nordicsemi.android.uart.view.OnDeleteMacro +import no.nordicsemi.android.uart.view.OnEditConfiguration +import no.nordicsemi.android.uart.view.OnEditFinish +import no.nordicsemi.android.uart.view.OnEditMacro +import no.nordicsemi.android.uart.view.OnRunInput +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 internal class UARTViewModel @Inject constructor( private val repository: UARTRepository, - private val navigationManager: NavigationManager, + private val navigationManager: Navigator, private val dataSource: UARTPersistentDataSource, private val analytics: AppAnalytics ) : ViewModel() { @@ -90,20 +122,18 @@ internal class UARTViewModel @Inject constructor( } private fun requestBluetoothDevice() { - navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(UART_SERVICE_UUID)) + navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(UART_SERVICE_UUID)) - navigationManager.recentResult.onEach { - if (it.destinationId == ScannerDestinationId) { - handleArgs(it) - } - }.launchIn(viewModelScope) + navigationManager.resultFrom(ScannerDestinationId) + .onEach { handleResult(it) } + .launchIn(viewModelScope) } - private fun handleArgs(args: DestinationResult) { - when (args) { - is CancelDestinationResult -> navigationManager.navigateUp() - is SuccessDestinationResult -> repository.launch(args.getDevice()) - }.exhaustive + private fun handleResult(result: NavigationResult) { + when (result) { + is NavigationResult.Cancelled -> navigationManager.navigateUp() + is NavigationResult.Success -> repository.launch(result.value) + } } fun onEvent(event: UARTViewEvent) { @@ -123,7 +153,7 @@ internal class UARTViewModel @Inject constructor( OpenLogger -> repository.openLogger() is OnRunInput -> sendText(event.text, event.newLineChar) MacroInputSwitchClick -> onMacroInputSwitch() - }.exhaustive + } } private fun runMacro(macro: UARTMacro) { diff --git a/settings.gradle.kts b/settings.gradle.kts index ebf82a29..0b43a5d6 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.2") + from("no.nordicsemi.android.gradle:version-catalog:1.2.3") } } } @@ -80,3 +80,4 @@ include(":lib_utils") //if (file('../Android-BLE-Library').exists()) { // includeBuild('../Android-BLE-Library') //} +include(":lib_scanner")