mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-19 23:44:24 +01:00
Migrate profiles to new libraries
This commit is contained in:
@@ -60,6 +60,7 @@ dependencies {
|
|||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
|
|
||||||
implementation(libs.nordic.core)
|
implementation(libs.nordic.core)
|
||||||
implementation(libs.nordic.theme)
|
implementation(libs.nordic.theme)
|
||||||
|
|||||||
@@ -33,34 +33,43 @@ package no.nordicsemi.android.nrftoolbox
|
|||||||
|
|
||||||
import no.nordicsemi.android.bps.view.BPSScreen
|
import no.nordicsemi.android.bps.view.BPSScreen
|
||||||
import no.nordicsemi.android.cgms.view.CGMScreen
|
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.csc.view.CSCScreen
|
||||||
import no.nordicsemi.android.gls.main.view.GLSScreen
|
import no.nordicsemi.android.gls.main.view.GLSScreen
|
||||||
import no.nordicsemi.android.hrs.view.HRSScreen
|
import no.nordicsemi.android.hrs.view.HRSScreen
|
||||||
import no.nordicsemi.android.hts.view.HTSScreen
|
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.nrftoolbox.view.HomeScreen
|
||||||
import no.nordicsemi.android.prx.view.PRXScreen
|
import no.nordicsemi.android.prx.view.PRXScreen
|
||||||
import no.nordicsemi.android.rscs.view.RSCSScreen
|
import no.nordicsemi.android.rscs.view.RSCSScreen
|
||||||
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestination
|
||||||
import no.nordicsemi.android.uart.view.UARTScreen
|
import no.nordicsemi.android.uart.view.UARTScreen
|
||||||
import no.nordicsemi.ui.scanner.navigation.view.FindDeviceScreen
|
|
||||||
|
|
||||||
val HomeDestinations = ComposeDestinations(HomeDestination.values().map { it.destination })
|
val HomeDestinationId = createSimpleDestination("home-destination")
|
||||||
val ProfileDestinations = ComposeDestinations(ProfileDestination.values().map { it.destination })
|
|
||||||
|
|
||||||
enum class HomeDestination(val destination: ComposeDestination) {
|
val HomeDestinations = listOf(
|
||||||
HOME(ComposeDestination("home-destination") { HomeScreen() }),
|
defineDestination(HomeDestinationId) { HomeScreen() },
|
||||||
SCANNER(ComposeDestination("scanner-destination") { FindDeviceScreen() });
|
ScannerDestination
|
||||||
}
|
)
|
||||||
|
|
||||||
enum class ProfileDestination(val destination: ComposeDestination) {
|
val CSCDestinationId = createSimpleDestination("csc-destination")
|
||||||
CSC(ComposeDestination("csc-destination") { CSCScreen() }),
|
val HRSDestinationId = createSimpleDestination("hrs-destination")
|
||||||
HRS(ComposeDestination("hrs-destination") { HRSScreen() }),
|
val HTSDestinationId = createSimpleDestination("hts-destination")
|
||||||
HTS(ComposeDestination("hts-destination") { HTSScreen() }),
|
val GLSDestinationId = createSimpleDestination("gls-destination")
|
||||||
GLS(ComposeDestination("gls-destination") { GLSScreen() }),
|
val BPSDestinationId = createSimpleDestination("bps-destination")
|
||||||
BPS(ComposeDestination("bps-destination") { BPSScreen() }),
|
val PRXDestinationId = createSimpleDestination("prx-destination")
|
||||||
PRX(ComposeDestination("prx-destination") { PRXScreen() }),
|
val RSCSDestinationId = createSimpleDestination("rscs-destination")
|
||||||
RSCS(ComposeDestination("rscs-destination") { RSCSScreen() }),
|
val CGMSDestinationId = createSimpleDestination("cgms-destination")
|
||||||
CGMS(ComposeDestination("cgms-destination") { CGMScreen() }),
|
val UARTDestinationId = createSimpleDestination("uart-destination")
|
||||||
UART(ComposeDestination("uart-destination") { UARTScreen() });
|
|
||||||
}
|
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() },
|
||||||
|
)
|
||||||
|
|||||||
@@ -38,12 +38,16 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
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.gls.GLSDestinations
|
||||||
import no.nordicsemi.android.theme.NordicActivity
|
|
||||||
import no.nordicsemi.android.theme.NordicTheme
|
|
||||||
import no.nordicsemi.android.navigation.NavigationView
|
import no.nordicsemi.android.navigation.NavigationView
|
||||||
import no.nordicsemi.android.nrftoolbox.repository.ActivitySignals
|
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 no.nordicsemi.ui.scanner.ScannerDestinations
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -62,7 +66,7 @@ class MainActivity : NordicActivity() {
|
|||||||
color = MaterialTheme.colorScheme.surface,
|
color = MaterialTheme.colorScheme.surface,
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
NavigationView(HomeDestinations + ProfileDestinations + ScannerDestinations + GLSDestinations)
|
NavigationView(HomeDestinations + ProfileDestinations + ScannerDestination + GLSDestination)
|
||||||
}
|
}
|
||||||
|
|
||||||
AnalyticsPermissionRequestDialog()
|
AnalyticsPermissionRequestDialog()
|
||||||
|
|||||||
@@ -48,9 +48,17 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||||||
import no.nordicsemi.android.analytics.Link
|
import no.nordicsemi.android.analytics.Link
|
||||||
import no.nordicsemi.android.analytics.Profile
|
import no.nordicsemi.android.analytics.Profile
|
||||||
import no.nordicsemi.android.analytics.ProfileOpenEvent
|
import no.nordicsemi.android.analytics.ProfileOpenEvent
|
||||||
|
import no.nordicsemi.android.nrftoolbox.BPSDestinationId
|
||||||
import no.nordicsemi.android.nrftoolbox.BuildConfig
|
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.R
|
||||||
|
import no.nordicsemi.android.nrftoolbox.RSCSDestinationId
|
||||||
|
import no.nordicsemi.android.nrftoolbox.UARTDestinationId
|
||||||
import no.nordicsemi.android.nrftoolbox.viewmodel.HomeViewModel
|
import no.nordicsemi.android.nrftoolbox.viewmodel.HomeViewModel
|
||||||
|
|
||||||
private const val DFU_PACKAGE_NAME = "no.nordicsemi.android.dfu"
|
private const val DFU_PACKAGE_NAME = "no.nordicsemi.android.dfu"
|
||||||
@@ -87,14 +95,14 @@ fun HomeScreen() {
|
|||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
FeatureButton(R.drawable.ic_gls, R.string.gls_module, R.string.gls_module_full) {
|
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))
|
viewModel.logEvent(ProfileOpenEvent(Profile.GLS))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
FeatureButton(R.drawable.ic_bps, R.string.bps_module, R.string.bps_module_full) {
|
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))
|
viewModel.logEvent(ProfileOpenEvent(Profile.BPS))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,42 +117,42 @@ fun HomeScreen() {
|
|||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
FeatureButton(R.drawable.ic_csc, R.string.csc_module, R.string.csc_module_full, state.isCSCModuleRunning) {
|
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))
|
viewModel.logEvent(ProfileOpenEvent(Profile.CSC))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
FeatureButton(R.drawable.ic_hrs, R.string.hrs_module, R.string.hrs_module_full, state.isHRSModuleRunning) {
|
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))
|
viewModel.logEvent(ProfileOpenEvent(Profile.HRS))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
FeatureButton(R.drawable.ic_hts, R.string.hts_module, R.string.hts_module_full, state.isHTSModuleRunning) {
|
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))
|
viewModel.logEvent(ProfileOpenEvent(Profile.HTS))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
FeatureButton(R.drawable.ic_rscs, R.string.rscs_module, R.string.rscs_module_full, state.isRSCSModuleRunning) {
|
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))
|
viewModel.logEvent(ProfileOpenEvent(Profile.RSCS))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
FeatureButton(R.drawable.ic_cgm, R.string.cgm_module, R.string.cgm_module_full, state.isCGMModuleRunning) {
|
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))
|
viewModel.logEvent(ProfileOpenEvent(Profile.CGMS))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
FeatureButton(R.drawable.ic_prx, R.string.prx_module, R.string.prx_module_full, state.isPRXModuleRunning) {
|
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))
|
viewModel.logEvent(ProfileOpenEvent(Profile.PRX))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +167,7 @@ fun HomeScreen() {
|
|||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
FeatureButton(R.drawable.ic_uart, R.string.uart_module, R.string.uart_module_full, state.isUARTModuleRunning) {
|
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))
|
viewModel.logEvent(ProfileOpenEvent(Profile.UART))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
import no.nordicsemi.android.analytics.AppAnalytics
|
import no.nordicsemi.android.analytics.AppAnalytics
|
||||||
import no.nordicsemi.android.analytics.ProfileOpenEvent
|
import no.nordicsemi.android.analytics.ProfileOpenEvent
|
||||||
import no.nordicsemi.android.cgms.repository.CGMRepository
|
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.csc.repository.CSCRepository
|
||||||
import no.nordicsemi.android.hrs.service.HRSRepository
|
import no.nordicsemi.android.hrs.service.HRSRepository
|
||||||
import no.nordicsemi.android.hts.repository.HTSRepository
|
import no.nordicsemi.android.hts.repository.HTSRepository
|
||||||
import no.nordicsemi.android.logger.LoggerAppRunner
|
import no.nordicsemi.android.common.navigation.Navigator
|
||||||
import no.nordicsemi.android.navigation.NavigationManager
|
|
||||||
import no.nordicsemi.android.nrftoolbox.ProfileDestination
|
|
||||||
import no.nordicsemi.android.nrftoolbox.repository.ActivitySignals
|
import no.nordicsemi.android.nrftoolbox.repository.ActivitySignals
|
||||||
import no.nordicsemi.android.nrftoolbox.view.HomeViewState
|
import no.nordicsemi.android.nrftoolbox.view.HomeViewState
|
||||||
import no.nordicsemi.android.prx.repository.PRXRepository
|
import no.nordicsemi.android.prx.repository.PRXRepository
|
||||||
@@ -56,7 +56,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class HomeViewModel @Inject constructor(
|
class HomeViewModel @Inject constructor(
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
private val activitySignals: ActivitySignals,
|
private val activitySignals: ActivitySignals,
|
||||||
cgmRepository: CGMRepository,
|
cgmRepository: CGMRepository,
|
||||||
cscRepository: CSCRepository,
|
cscRepository: CSCRepository,
|
||||||
@@ -65,7 +65,6 @@ class HomeViewModel @Inject constructor(
|
|||||||
prxRepository: PRXRepository,
|
prxRepository: PRXRepository,
|
||||||
rscsRepository: RSCSRepository,
|
rscsRepository: RSCSRepository,
|
||||||
uartRepository: UARTRepository,
|
uartRepository: UARTRepository,
|
||||||
private val loggerAppRunner: LoggerAppRunner,
|
|
||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -106,11 +105,12 @@ class HomeViewModel @Inject constructor(
|
|||||||
}.launchIn(viewModelScope)
|
}.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openProfile(destination: ProfileDestination) {
|
fun openProfile(destination: DestinationId<Unit, Unit>) {
|
||||||
navigationManager.navigateTo(destination.destination.id)
|
navigationManager.navigateTo(destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
|
NordicLogger.Companion.launch()
|
||||||
loggerAppRunner.runLogger()
|
loggerAppRunner.runLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.jvm) apply false
|
alias(libs.plugins.kotlin.jvm) apply false
|
||||||
alias(libs.plugins.kotlin.kapt) apply true
|
alias(libs.plugins.kotlin.kapt) apply true
|
||||||
alias(libs.plugins.kotlin.serialization) apply false
|
alias(libs.plugins.kotlin.serialization) apply false
|
||||||
|
alias(libs.plugins.kotlin.parcelize) apply false
|
||||||
alias(libs.plugins.hilt) apply false
|
alias(libs.plugins.hilt) apply false
|
||||||
alias(libs.plugins.secrets) apply false
|
alias(libs.plugins.secrets) apply false
|
||||||
alias(libs.plugins.protobuf) 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.library.compose) apply false
|
||||||
alias(libs.plugins.nordic.hilt) apply false
|
alias(libs.plugins.nordic.hilt) apply false
|
||||||
alias(libs.plugins.nordic.feature) 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
|
||||||
}
|
}
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
#Mon Feb 14 14:46:55 CET 2022
|
#Mon Feb 14 14:46:55 CET 2022
|
||||||
distributionBase=GRADLE_USER_HOME
|
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
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
package no.nordicsemi.android.analytics
|
package no.nordicsemi.android.analytics
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import no.nordicsemi.analytics.NordicAnalytics
|
import no.nordicsemi.android.common.analytics.NordicAnalytics
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|||||||
48
lib_scanner/build.gradle.kts
Normal file
48
lib_scanner/build.gradle.kts
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
4
lib_scanner/src/main/AndroidManifest.xml
Normal file
4
lib_scanner/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -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<ParcelUuid, DiscoveredBluetoothDevice>("uiscanner-destination")
|
||||||
|
|
||||||
|
val ScannerDestination = defineDestination(ScannerDestinationId) {
|
||||||
|
val navigationViewModel = hiltViewModel<SimpleNavigationViewModel>()
|
||||||
|
|
||||||
|
val arg = navigationViewModel.parameterOf(ScannerDestinationId)
|
||||||
|
|
||||||
|
ScannerScreen(
|
||||||
|
uuid = arg,
|
||||||
|
onResult = {
|
||||||
|
when (it) {
|
||||||
|
is DeviceSelected -> navigationViewModel.navigateUpWithResult(ScannerDestinationId, it.device)
|
||||||
|
ScanningCancelled -> navigationViewModel.navigateUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,4 +46,5 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.androidx.lifecycle.service)
|
implementation(libs.androidx.lifecycle.service)
|
||||||
implementation(libs.androidx.localbroadcastmanager)
|
implementation(libs.androidx.localbroadcastmanager)
|
||||||
|
implementation(libs.androidx.core)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import android.bluetooth.BluetoothDevice
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
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
|
import javax.inject.Inject
|
||||||
|
|
||||||
const val DEVICE_DATA = "device-data"
|
const val DEVICE_DATA = "device-data"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,14 +31,14 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.ui.view
|
package no.nordicsemi.android.ui.view
|
||||||
|
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import no.nordicsemi.android.theme.ScreenSection
|
|
||||||
import no.nordicsemi.android.ui.R
|
import no.nordicsemi.android.ui.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BatteryLevelView(batteryLevel: Int) {
|
fun BatteryLevelView(batteryLevel: Int) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
KeyValueField(
|
KeyValueField(
|
||||||
stringResource(id = R.string.field_battery),
|
stringResource(id = R.string.field_battery),
|
||||||
"$batteryLevel%"
|
"$batteryLevel%"
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.ui.R
|
import no.nordicsemi.android.ui.R
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CloseIconAppBar(text: String, onClick: () -> Unit) {
|
fun CloseIconAppBar(text: String, onClick: () -> Unit) {
|
||||||
SmallTopAppBar(
|
SmallTopAppBar(
|
||||||
@@ -67,6 +68,7 @@ fun CloseIconAppBar(text: String, onClick: () -> Unit) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun LoggerBackIconAppBar(text: String, onClick: () -> Unit) {
|
fun LoggerBackIconAppBar(text: String, onClick: () -> Unit) {
|
||||||
SmallTopAppBar(
|
SmallTopAppBar(
|
||||||
@@ -100,6 +102,7 @@ fun LoggerBackIconAppBar(text: String, onClick: () -> Unit) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BackIconAppBar(text: String, onClick: () -> Unit) {
|
fun BackIconAppBar(text: String, onClick: () -> Unit) {
|
||||||
SmallTopAppBar(
|
SmallTopAppBar(
|
||||||
@@ -123,6 +126,7 @@ fun BackIconAppBar(text: String, onClick: () -> Unit) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun LoggerIconAppBar(text: String, onClick: () -> Unit, onDisconnectClick: () -> Unit, onLoggerClick: () -> Unit) {
|
fun LoggerIconAppBar(text: String, onClick: () -> Unit, onDisconnectClick: () -> Unit, onLoggerClick: () -> Unit) {
|
||||||
SmallTopAppBar(
|
SmallTopAppBar(
|
||||||
|
|||||||
@@ -41,4 +41,5 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.nordic.navigation)
|
implementation(libs.nordic.navigation)
|
||||||
implementation(libs.nordic.uiscanner)
|
implementation(libs.nordic.uiscanner)
|
||||||
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,12 +38,6 @@ import kotlinx.coroutines.CoroutineExceptionHandler
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.navigation.ParcelableArgument
|
|
||||||
import no.nordicsemi.android.navigation.SuccessDestinationResult
|
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
|
|
||||||
val <T> T.exhaustive
|
|
||||||
get() = this
|
|
||||||
|
|
||||||
val String.Companion.EMPTY
|
val String.Companion.EMPTY
|
||||||
get() = ""
|
get() = ""
|
||||||
@@ -54,10 +48,6 @@ fun Context.isServiceRunning(serviceClassName: String): Boolean {
|
|||||||
return services.find { it.service.className == serviceClassName } != null
|
return services.find { it.service.className == serviceClassName } != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun SuccessDestinationResult.getDevice(): DiscoveredBluetoothDevice {
|
|
||||||
return (argument as ParcelableArgument).value as DiscoveredBluetoothDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
private val exceptionHandler = CoroutineExceptionHandler { _, t ->
|
private val exceptionHandler = CoroutineExceptionHandler { _, t ->
|
||||||
Log.e("COROUTINE-EXCEPTION", "Uncaught exception", t)
|
Log.e("COROUTINE-EXCEPTION", "Uncaught exception", t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":lib_analytics"))
|
implementation(project(":lib_analytics"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ dependencies {
|
|||||||
implementation(libs.nordic.uiscanner)
|
implementation(libs.nordic.uiscanner)
|
||||||
|
|
||||||
implementation(libs.androidx.compose.material.iconsExtended)
|
implementation(libs.androidx.compose.material.iconsExtended)
|
||||||
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
|
|||||||
@@ -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.BloodPressureMeasurementResponse
|
||||||
import no.nordicsemi.android.ble.common.callback.bps.IntermediateCuffPressureResponse
|
import no.nordicsemi.android.ble.common.callback.bps.IntermediateCuffPressureResponse
|
||||||
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
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 no.nordicsemi.android.service.ConnectionObserverAdapter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|||||||
@@ -44,11 +44,11 @@ import kotlinx.coroutines.launch
|
|||||||
import no.nordicsemi.android.ble.ktx.suspend
|
import no.nordicsemi.android.ble.ktx.suspend
|
||||||
import no.nordicsemi.android.bps.data.BPSData
|
import no.nordicsemi.android.bps.data.BPSData
|
||||||
import no.nordicsemi.android.bps.data.BPSManager
|
import no.nordicsemi.android.bps.data.BPSManager
|
||||||
import no.nordicsemi.android.logger.NordicLogger
|
import no.nordicsemi.android.common.logger.NordicLogger
|
||||||
import no.nordicsemi.android.logger.NordicLoggerFactory
|
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.BleManagerResult
|
||||||
import no.nordicsemi.android.ui.view.StringConst
|
import no.nordicsemi.android.ui.view.StringConst
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ViewModelScoped
|
@ViewModelScoped
|
||||||
@@ -62,7 +62,7 @@ internal class BPSRepository @Inject constructor(
|
|||||||
private var logger: NordicLogger? = null
|
private var logger: NordicLogger? = null
|
||||||
|
|
||||||
fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow<BleManagerResult<BPSData>> = callbackFlow {
|
fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow<BleManagerResult<BPSData>> = 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
|
logger = it
|
||||||
}
|
}
|
||||||
val manager = BPSManager(context, scope, createdLogger)
|
val manager = BPSManager(context, scope, createdLogger)
|
||||||
@@ -93,7 +93,6 @@ internal class BPSRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
logger?.openLogger()
|
NordicLogger.launch(context, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,14 +41,21 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import no.nordicsemi.android.bps.R
|
import no.nordicsemi.android.bps.R
|
||||||
import no.nordicsemi.android.bps.viewmodel.BPSViewModel
|
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.BackIconAppBar
|
||||||
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.ui.view.NavigateUpButton
|
||||||
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
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BPSScreen() {
|
fun BPSScreen() {
|
||||||
@@ -62,18 +69,18 @@ fun BPSScreen() {
|
|||||||
|
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
when (state) {
|
when (state) {
|
||||||
NoDeviceState -> NoDeviceView()
|
NoDeviceState -> DeviceConnectingView()
|
||||||
is WorkingState -> when (state.result) {
|
is WorkingState -> when (state.result) {
|
||||||
is IdleResult,
|
is IdleResult,
|
||||||
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
|
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) }
|
||||||
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
|
||||||
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) }
|
||||||
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
||||||
is SuccessResult -> BPSContentView(state.result.data) { viewModel.onEvent(it) }
|
is SuccessResult -> BPSContentView(state.result.data) { viewModel.onEvent(it) }
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ package no.nordicsemi.android.bps.view
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -41,14 +42,13 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.bps.R
|
import no.nordicsemi.android.bps.R
|
||||||
import no.nordicsemi.android.bps.data.BPSData
|
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.BatteryLevelView
|
||||||
import no.nordicsemi.android.ui.view.KeyValueField
|
import no.nordicsemi.android.ui.view.KeyValueField
|
||||||
import no.nordicsemi.android.ui.view.SectionTitle
|
import no.nordicsemi.android.ui.view.SectionTitle
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun BPSSensorsReadingView(state: BPSData) {
|
internal fun BPSSensorsReadingView(state: BPSData) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
Column {
|
Column {
|
||||||
SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.bps_records))
|
SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.bps_records))
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.bps.viewmodel
|
package no.nordicsemi.android.bps.viewmodel
|
||||||
|
|
||||||
|
import android.os.ParcelUuid
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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.analytics.ProfileConnectedEvent
|
||||||
import no.nordicsemi.android.bps.data.BPS_SERVICE_UUID
|
import no.nordicsemi.android.bps.data.BPS_SERVICE_UUID
|
||||||
import no.nordicsemi.android.bps.repository.BPSRepository
|
import no.nordicsemi.android.bps.repository.BPSRepository
|
||||||
import no.nordicsemi.android.bps.view.*
|
import no.nordicsemi.android.bps.view.BPSViewEvent
|
||||||
import no.nordicsemi.android.navigation.*
|
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.service.ConnectedResult
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
||||||
import no.nordicsemi.android.utils.getDevice
|
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import no.nordicsemi.ui.scanner.ScannerDestinationId
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class BPSViewModel @Inject constructor(
|
internal class BPSViewModel @Inject constructor(
|
||||||
private val repository: BPSRepository,
|
private val repository: BPSRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -63,27 +68,25 @@ internal class BPSViewModel @Inject constructor(
|
|||||||
val state = _state.asStateFlow()
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(BPS_SERVICE_UUID))
|
navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(BPS_SERVICE_UUID))
|
||||||
|
|
||||||
navigationManager.recentResult.onEach {
|
navigationManager.resultFrom(ScannerDestinationId)
|
||||||
if (it.destinationId == ScannerDestinationId) {
|
.onEach { handleArgs(it) }
|
||||||
handleArgs(it)
|
.launchIn(viewModelScope)
|
||||||
}
|
|
||||||
}.launchIn(viewModelScope)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleArgs(args: DestinationResult) {
|
private fun handleArgs(result: NavigationResult<DiscoveredBluetoothDevice>) {
|
||||||
when (args) {
|
when (result) {
|
||||||
is CancelDestinationResult -> navigationManager.navigateUp()
|
is NavigationResult.Cancelled -> navigationManager.navigateUp()
|
||||||
is SuccessDestinationResult -> connectDevice(args.getDevice())
|
is NavigationResult.Success -> connectDevice(result.value)
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEvent(event: BPSViewEvent) {
|
fun onEvent(event: BPSViewEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
DisconnectEvent -> navigationManager.navigateUp()
|
DisconnectEvent -> navigationManager.navigateUp()
|
||||||
OpenLoggerEvent -> repository.openLogger()
|
OpenLoggerEvent -> repository.openLogger()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun connectDevice(device: DiscoveredBluetoothDevice) {
|
private fun connectDevice(device: DiscoveredBluetoothDevice) {
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.parcelize)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -41,6 +41,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":lib_analytics"))
|
implementation(project(":lib_analytics"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ dependencies {
|
|||||||
implementation(libs.nordic.uiscanner)
|
implementation(libs.nordic.uiscanner)
|
||||||
implementation(libs.nordic.navigation)
|
implementation(libs.nordic.navigation)
|
||||||
|
|
||||||
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
implementation(libs.androidx.compose.material.iconsExtended)
|
implementation(libs.androidx.compose.material.iconsExtended)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
|
|||||||
@@ -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.asValidResponseFlow
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
import no.nordicsemi.android.ble.ktx.suspend
|
||||||
import no.nordicsemi.android.ble.ktx.suspendForValidResponse
|
import no.nordicsemi.android.ble.ktx.suspendForValidResponse
|
||||||
import no.nordicsemi.android.cgms.repository.toList
|
import no.nordicsemi.android.common.logger.NordicLogger
|
||||||
import no.nordicsemi.android.logger.NordicLogger
|
|
||||||
import no.nordicsemi.android.service.ConnectionObserverAdapter
|
import no.nordicsemi.android.service.ConnectionObserverAdapter
|
||||||
import no.nordicsemi.android.utils.launchWithCatch
|
import no.nordicsemi.android.utils.launchWithCatch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|||||||
@@ -29,11 +29,10 @@
|
|||||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* 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 android.util.SparseArray
|
||||||
import androidx.core.util.keyIterator
|
import androidx.core.util.keyIterator
|
||||||
import no.nordicsemi.android.cgms.data.CGMRecord
|
|
||||||
|
|
||||||
internal fun SparseArray<CGMRecord>.toList(): List<CGMRecord> {
|
internal fun SparseArray<CGMRecord>.toList(): List<CGMRecord> {
|
||||||
val list = mutableListOf<CGMRecord>()
|
val list = mutableListOf<CGMRecord>()
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ import kotlinx.coroutines.launch
|
|||||||
import no.nordicsemi.android.ble.ktx.suspend
|
import no.nordicsemi.android.ble.ktx.suspend
|
||||||
import no.nordicsemi.android.cgms.data.CGMData
|
import no.nordicsemi.android.cgms.data.CGMData
|
||||||
import no.nordicsemi.android.cgms.data.CGMManager
|
import no.nordicsemi.android.cgms.data.CGMManager
|
||||||
import no.nordicsemi.android.logger.NordicLogger
|
import no.nordicsemi.android.common.logger.NordicLogger
|
||||||
import no.nordicsemi.android.logger.NordicLoggerFactory
|
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.BleManagerResult
|
||||||
import no.nordicsemi.android.service.IdleResult
|
import no.nordicsemi.android.service.IdleResult
|
||||||
import no.nordicsemi.android.service.ServiceManager
|
import no.nordicsemi.android.service.ServiceManager
|
||||||
import no.nordicsemi.android.ui.view.StringConst
|
import no.nordicsemi.android.ui.view.StringConst
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ class CGMRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) {
|
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
|
logger = it
|
||||||
}
|
}
|
||||||
val manager = CGMManager(context, scope, createdLogger)
|
val manager = CGMManager(context, scope, createdLogger)
|
||||||
@@ -110,7 +110,7 @@ class CGMRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
logger?.openLogger()
|
NordicLogger.launch(context, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun release() {
|
fun release() {
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
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.DEVICE_DATA
|
||||||
import no.nordicsemi.android.service.NotificationService
|
import no.nordicsemi.android.service.NotificationService
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import androidx.compose.material.icons.filled.Settings
|
|||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
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.CGMRecord
|
||||||
import no.nordicsemi.android.cgms.data.CGMServiceCommand
|
import no.nordicsemi.android.cgms.data.CGMServiceCommand
|
||||||
import no.nordicsemi.android.cgms.data.RequestStatus
|
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.BatteryLevelView
|
||||||
import no.nordicsemi.android.ui.view.SectionTitle
|
import no.nordicsemi.android.ui.view.SectionTitle
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ internal fun CGMContentView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SettingsView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) {
|
private fun SettingsView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
SectionTitle(icon = Icons.Default.Settings, title = "Request items")
|
SectionTitle(icon = Icons.Default.Settings, title = "Request items")
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
@@ -115,7 +115,7 @@ private fun SettingsView(state: CGMData, onEvent: (CGMViewEvent) -> Unit) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RecordsView(state: CGMData) {
|
private fun RecordsView(state: CGMData) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
if (state.records.isEmpty()) {
|
if (state.records.isEmpty()) {
|
||||||
RecordsViewWithoutData()
|
RecordsViewWithoutData()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -41,14 +41,21 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import no.nordicsemi.android.cgms.R
|
import no.nordicsemi.android.cgms.R
|
||||||
import no.nordicsemi.android.cgms.viewmodel.CGMViewModel
|
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.BackIconAppBar
|
||||||
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.ui.view.NavigateUpButton
|
||||||
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
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CGMScreen() {
|
fun CGMScreen() {
|
||||||
@@ -62,18 +69,18 @@ fun CGMScreen() {
|
|||||||
|
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
when (state) {
|
when (state) {
|
||||||
NoDeviceState -> NoDeviceView()
|
NoDeviceState -> DeviceConnectingView()
|
||||||
is WorkingState -> when (state.result) {
|
is WorkingState -> when (state.result) {
|
||||||
is IdleResult,
|
is IdleResult,
|
||||||
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
|
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) }
|
||||||
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
|
||||||
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) }
|
||||||
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
||||||
is SuccessResult -> CGMContentView(state.result.data) { viewModel.onEvent(it) }
|
is SuccessResult -> CGMContentView(state.result.data) { viewModel.onEvent(it) }
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,15 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.cgms.viewmodel
|
package no.nordicsemi.android.cgms.viewmodel
|
||||||
|
|
||||||
|
import android.os.ParcelUuid
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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 kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.analytics.AppAnalytics
|
import no.nordicsemi.android.analytics.AppAnalytics
|
||||||
import no.nordicsemi.android.analytics.Profile
|
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.CGMS_SERVICE_UUID
|
||||||
import no.nordicsemi.android.cgms.data.CGMServiceCommand
|
import no.nordicsemi.android.cgms.data.CGMServiceCommand
|
||||||
import no.nordicsemi.android.cgms.repository.CGMRepository
|
import no.nordicsemi.android.cgms.repository.CGMRepository
|
||||||
import no.nordicsemi.android.cgms.view.*
|
import no.nordicsemi.android.cgms.view.CGMViewEvent
|
||||||
import no.nordicsemi.android.navigation.*
|
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.service.ConnectedResult
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
||||||
import no.nordicsemi.android.utils.getDevice
|
|
||||||
import no.nordicsemi.ui.scanner.ScannerDestinationId
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class CGMViewModel @Inject constructor(
|
internal class CGMViewModel @Inject constructor(
|
||||||
private val repository: CGMRepository,
|
private val repository: CGMRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -82,24 +94,22 @@ internal class CGMViewModel @Inject constructor(
|
|||||||
is OnWorkingModeSelected -> onCommandReceived(event.workingMode)
|
is OnWorkingModeSelected -> onCommandReceived(event.workingMode)
|
||||||
NavigateUp -> navigationManager.navigateUp()
|
NavigateUp -> navigationManager.navigateUp()
|
||||||
OpenLoggerEvent -> repository.openLogger()
|
OpenLoggerEvent -> repository.openLogger()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requestBluetoothDevice() {
|
private fun requestBluetoothDevice() {
|
||||||
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(CGMS_SERVICE_UUID))
|
navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(CGMS_SERVICE_UUID))
|
||||||
|
|
||||||
navigationManager.recentResult.onEach {
|
navigationManager.resultFrom(ScannerDestinationId)
|
||||||
if (it.destinationId == ScannerDestinationId) {
|
.onEach { handleResult(it) }
|
||||||
handleArgs(it)
|
.launchIn(viewModelScope)
|
||||||
}
|
|
||||||
}.launchIn(viewModelScope)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleArgs(args: DestinationResult) {
|
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) {
|
||||||
when (args) {
|
when (result) {
|
||||||
is CancelDestinationResult -> navigationManager.navigateUp()
|
is NavigationResult.Cancelled -> navigationManager.navigateUp()
|
||||||
is SuccessDestinationResult -> repository.launch(args.getDevice())
|
is NavigationResult.Success -> repository.launch(result.value)
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCommandReceived(workingMode: CGMServiceCommand) {
|
private fun onCommandReceived(workingMode: CGMServiceCommand) {
|
||||||
@@ -108,7 +118,7 @@ internal class CGMViewModel @Inject constructor(
|
|||||||
CGMServiceCommand.REQUEST_LAST_RECORD -> repository.requestLastRecord()
|
CGMServiceCommand.REQUEST_LAST_RECORD -> repository.requestLastRecord()
|
||||||
CGMServiceCommand.REQUEST_FIRST_RECORD -> repository.requestFirstRecord()
|
CGMServiceCommand.REQUEST_FIRST_RECORD -> repository.requestFirstRecord()
|
||||||
CGMServiceCommand.DISCONNECT -> disconnect()
|
CGMServiceCommand.DISCONNECT -> disconnect()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun disconnect() {
|
private fun disconnect() {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":lib_analytics"))
|
implementation(project(":lib_analytics"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ dependencies {
|
|||||||
implementation(libs.nordic.navigation)
|
implementation(libs.nordic.navigation)
|
||||||
implementation(libs.nordic.uiscanner)
|
implementation(libs.nordic.uiscanner)
|
||||||
|
|
||||||
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
implementation(libs.androidx.compose.material.iconsExtended)
|
implementation(libs.androidx.compose.material.iconsExtended)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
|
|||||||
@@ -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.battery.BatteryLevelResponse
|
||||||
import no.nordicsemi.android.ble.common.callback.csc.CyclingSpeedAndCadenceMeasurementResponse
|
import no.nordicsemi.android.ble.common.callback.csc.CyclingSpeedAndCadenceMeasurementResponse
|
||||||
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
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 no.nordicsemi.android.service.ConnectionObserverAdapter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|||||||
@@ -37,16 +37,16 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
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.CSCData
|
||||||
import no.nordicsemi.android.csc.data.CSCManager
|
import no.nordicsemi.android.csc.data.CSCManager
|
||||||
import no.nordicsemi.android.csc.data.WheelSize
|
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.BleManagerResult
|
||||||
import no.nordicsemi.android.service.IdleResult
|
import no.nordicsemi.android.service.IdleResult
|
||||||
import no.nordicsemi.android.service.ServiceManager
|
import no.nordicsemi.android.service.ServiceManager
|
||||||
import no.nordicsemi.android.ui.view.StringConst
|
import no.nordicsemi.android.ui.view.StringConst
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ class CSCRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) {
|
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
|
logger = it
|
||||||
}
|
}
|
||||||
val manager = CSCManager(context, scope, createdLogger)
|
val manager = CSCManager(context, scope, createdLogger)
|
||||||
@@ -103,7 +103,7 @@ class CSCRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
logger?.openLogger()
|
NordicLogger.launch(context, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun release() {
|
fun release() {
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
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.DEVICE_DATA
|
||||||
import no.nordicsemi.android.service.NotificationService
|
import no.nordicsemi.android.service.NotificationService
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
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.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
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.R
|
||||||
import no.nordicsemi.android.csc.data.CSCData
|
import no.nordicsemi.android.csc.data.CSCData
|
||||||
import no.nordicsemi.android.csc.data.WheelSize
|
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.SectionTitle
|
||||||
import no.nordicsemi.android.ui.view.dialog.FlowCanceled
|
import no.nordicsemi.android.ui.view.dialog.FlowCanceled
|
||||||
import no.nordicsemi.android.ui.view.dialog.ItemSelectedResult
|
import no.nordicsemi.android.ui.view.dialog.ItemSelectedResult
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun CSCContentView(state: CSCData, speedUnit: SpeedUnit, onEvent: (CSCViewEvent) -> Unit) {
|
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])))
|
wheelEntries[it.index])))
|
||||||
showDialog.value = false
|
showDialog.value = false
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +104,7 @@ private fun SettingsSection(
|
|||||||
onEvent: (CSCViewEvent) -> Unit,
|
onEvent: (CSCViewEvent) -> Unit,
|
||||||
onWheelButtonClick: () -> Unit,
|
onWheelButtonClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -31,9 +31,9 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.csc.view
|
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.csc.data.CSCData
|
||||||
import no.nordicsemi.android.theme.RadioButtonItem
|
|
||||||
import no.nordicsemi.android.theme.RadioGroupViewEntity
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
private const val DISPLAY_M_S = "m/s"
|
private const val DISPLAY_M_S = "m/s"
|
||||||
|
|||||||
@@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
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.R
|
||||||
import no.nordicsemi.android.csc.viewmodel.CSCViewModel
|
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.BackIconAppBar
|
||||||
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.ui.view.NavigateUpButton
|
||||||
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
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CSCScreen() {
|
fun CSCScreen() {
|
||||||
@@ -62,18 +69,18 @@ fun CSCScreen() {
|
|||||||
|
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
when (state.cscManagerState) {
|
when (state.cscManagerState) {
|
||||||
NoDeviceState -> NoDeviceView()
|
NoDeviceState -> DeviceConnectingView()
|
||||||
is WorkingState -> when (state.cscManagerState.result) {
|
is WorkingState -> when (state.cscManagerState.result) {
|
||||||
is IdleResult,
|
is IdleResult,
|
||||||
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(OnDisconnectButtonClick) }
|
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(OnDisconnectButtonClick) }
|
||||||
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(OnDisconnectButtonClick) }
|
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(OnDisconnectButtonClick) }
|
||||||
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
|
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) }
|
||||||
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
|
||||||
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) }
|
||||||
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
||||||
is SuccessResult -> CSCContentView(state.cscManagerState.result.data, state.speedUnit) { viewModel.onEvent(it) }
|
is SuccessResult -> CSCContentView(state.cscManagerState.result.data, state.speedUnit) { viewModel.onEvent(it) }
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.res.stringArrayResource
|
import androidx.compose.ui.res.stringArrayResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import no.nordicsemi.android.common.theme.NordicTheme
|
||||||
import no.nordicsemi.android.csc.R
|
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.StringListDialog
|
||||||
import no.nordicsemi.android.ui.view.dialog.StringListDialogConfig
|
import no.nordicsemi.android.ui.view.dialog.StringListDialogConfig
|
||||||
import no.nordicsemi.android.ui.view.dialog.StringListDialogResult
|
import no.nordicsemi.android.ui.view.dialog.StringListDialogResult
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ package no.nordicsemi.android.csc.view
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -41,14 +42,13 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.csc.R
|
import no.nordicsemi.android.csc.R
|
||||||
import no.nordicsemi.android.csc.data.CSCData
|
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.BatteryLevelView
|
||||||
import no.nordicsemi.android.ui.view.KeyValueField
|
import no.nordicsemi.android.ui.view.KeyValueField
|
||||||
import no.nordicsemi.android.ui.view.SectionTitle
|
import no.nordicsemi.android.ui.view.SectionTitle
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SensorsReadingView(state: CSCData, speedUnit: SpeedUnit) {
|
internal fun SensorsReadingView(state: CSCData, speedUnit: SpeedUnit) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
SectionTitle(resId = R.drawable.ic_records, title = "Records")
|
SectionTitle(resId = R.drawable.ic_records, title = "Records")
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.csc.viewmodel
|
package no.nordicsemi.android.csc.viewmodel
|
||||||
|
|
||||||
|
import android.os.ParcelUuid
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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.AppAnalytics
|
||||||
import no.nordicsemi.android.analytics.Profile
|
import no.nordicsemi.android.analytics.Profile
|
||||||
import no.nordicsemi.android.analytics.ProfileConnectedEvent
|
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.data.CSC_SERVICE_UUID
|
||||||
import no.nordicsemi.android.csc.repository.CSCRepository
|
import no.nordicsemi.android.csc.repository.CSCRepository
|
||||||
import no.nordicsemi.android.csc.view.*
|
import no.nordicsemi.android.csc.view.*
|
||||||
import no.nordicsemi.android.navigation.*
|
|
||||||
import no.nordicsemi.android.service.ConnectedResult
|
import no.nordicsemi.android.service.ConnectedResult
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
||||||
import no.nordicsemi.android.utils.getDevice
|
|
||||||
import no.nordicsemi.ui.scanner.ScannerDestinationId
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class CSCViewModel @Inject constructor(
|
internal class CSCViewModel @Inject constructor(
|
||||||
private val repository: CSCRepository,
|
private val repository: CSCRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -76,20 +77,18 @@ internal class CSCViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun requestBluetoothDevice() {
|
private fun requestBluetoothDevice() {
|
||||||
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(CSC_SERVICE_UUID))
|
navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(CSC_SERVICE_UUID))
|
||||||
|
|
||||||
navigationManager.recentResult.onEach {
|
navigationManager.resultFrom(ScannerDestinationId)
|
||||||
if (it.destinationId == ScannerDestinationId) {
|
.onEach { handleResult(it) }
|
||||||
handleArgs(it)
|
.launchIn(viewModelScope)
|
||||||
}
|
|
||||||
}.launchIn(viewModelScope)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleArgs(args: DestinationResult) {
|
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) {
|
||||||
when (args) {
|
when (result) {
|
||||||
is CancelDestinationResult -> navigationManager.navigateUp()
|
is NavigationResult.Cancelled -> navigationManager.navigateUp()
|
||||||
is SuccessDestinationResult -> repository.launch(args.getDevice())
|
is NavigationResult.Success -> repository.launch(result.value)
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEvent(event: CSCViewEvent) {
|
fun onEvent(event: CSCViewEvent) {
|
||||||
@@ -99,7 +98,7 @@ internal class CSCViewModel @Inject constructor(
|
|||||||
OnDisconnectButtonClick -> disconnect()
|
OnDisconnectButtonClick -> disconnect()
|
||||||
NavigateUp -> navigationManager.navigateUp()
|
NavigateUp -> navigationManager.navigateUp()
|
||||||
OpenLogger -> repository.openLogger()
|
OpenLogger -> repository.openLogger()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSpeedUnit(speedUnit: SpeedUnit) {
|
private fun setSpeedUnit(speedUnit: SpeedUnit) {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":lib_analytics"))
|
implementation(project(":lib_analytics"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ dependencies {
|
|||||||
implementation(libs.nordic.navigation)
|
implementation(libs.nordic.navigation)
|
||||||
implementation(libs.nordic.uilogger)
|
implementation(libs.nordic.uilogger)
|
||||||
|
|
||||||
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
implementation(libs.androidx.compose.material.iconsExtended)
|
implementation(libs.androidx.compose.material.iconsExtended)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
|
|||||||
@@ -31,13 +31,11 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.gls
|
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.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<GLSRecord, Unit>("gls-details-screen")
|
||||||
|
|
||||||
private val destination: ComposeDestination = ComposeDestination(GlsDetailsDestinationId) { GLSDetailsScreen() }
|
val GLSDestination = defineDestination(GlsDetailsDestinationId) { GLSDetailsScreen() }
|
||||||
|
|
||||||
val GLSDestinations = ComposeDestinations(destination)
|
|
||||||
|
|||||||
@@ -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.common.data.RecordAccessControlPointData
|
||||||
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
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.service.ConnectionObserverAdapter
|
||||||
import no.nordicsemi.android.utils.launchWithCatch
|
import no.nordicsemi.android.utils.launchWithCatch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ internal data class GLSRecord(
|
|||||||
val time: Calendar? = null,
|
val time: Calendar? = null,
|
||||||
val glucoseConcentration: Float = 0f,
|
val glucoseConcentration: Float = 0f,
|
||||||
val unit: ConcentrationUnit = ConcentrationUnit.UNIT_KGPL,
|
val unit: ConcentrationUnit = ConcentrationUnit.UNIT_KGPL,
|
||||||
val type: RecordType?,
|
val type: RecordType? = null,
|
||||||
val status: GlucoseStatus?,
|
val status: GlucoseStatus? = null,
|
||||||
val sampleLocation: SampleLocation? = null,
|
val sampleLocation: SampleLocation? = null,
|
||||||
var context: MeasurementContext? = null
|
var context: MeasurementContext? = null
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ internal fun BooleanField(title: String, value: Boolean) {
|
|||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.gls_no),
|
text = stringResource(id = R.string.gls_no),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = colorResource(id = no.nordicsemi.android.theme.R.color.nordicGrass)
|
color = colorResource(id = R.color.nordicGrass)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
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.R
|
||||||
import no.nordicsemi.android.gls.data.GLSRecord
|
import no.nordicsemi.android.gls.data.GLSRecord
|
||||||
import no.nordicsemi.android.gls.main.view.toDisplayString
|
import no.nordicsemi.android.gls.main.view.toDisplayString
|
||||||
import no.nordicsemi.android.theme.ScreenSection
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun GLSDetailsContentView(record: GLSRecord) {
|
internal fun GLSDetailsContentView(record: GLSRecord) {
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
Column(modifier = Modifier.padding(16.dp)) {
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
ScreenSection() {
|
OutlinedCard {
|
||||||
Field(
|
Field(
|
||||||
stringResource(id = R.string.gls_details_sequence_number),
|
stringResource(id = R.string.gls_details_sequence_number),
|
||||||
record.sequenceNumber.toString()
|
record.sequenceNumber.toString()
|
||||||
|
|||||||
@@ -31,21 +31,23 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.gls.details.viewmodel
|
package no.nordicsemi.android.gls.details.viewmodel
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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.GlsDetailsDestinationId
|
||||||
import no.nordicsemi.android.gls.data.GLSRecord
|
|
||||||
import no.nordicsemi.android.navigation.AnyArgument
|
|
||||||
import no.nordicsemi.android.navigation.NavigationManager
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class GLSDetailsViewModel @Inject constructor(
|
internal class GLSDetailsViewModel @Inject constructor(
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
) : ViewModel() {
|
private val savedStateHandle: SavedStateHandle
|
||||||
|
) : SimpleNavigationViewModel(navigationManager, savedStateHandle) {
|
||||||
|
|
||||||
val record =
|
private val _record = MutableStateFlow(parameterOf(GlsDetailsDestinationId))
|
||||||
(navigationManager.getImmediateArgument(GlsDetailsDestinationId) as AnyArgument).value as GLSRecord
|
val record = _record.asStateFlow()
|
||||||
|
|
||||||
fun navigateBack() {
|
fun navigateBack() {
|
||||||
navigationManager.navigateUp()
|
navigationManager.navigateUp()
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import androidx.compose.material.icons.filled.Settings
|
|||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
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.RequestStatus
|
||||||
import no.nordicsemi.android.gls.data.WorkingMode
|
import no.nordicsemi.android.gls.data.WorkingMode
|
||||||
import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel
|
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.BatteryLevelView
|
||||||
import no.nordicsemi.android.ui.view.SectionTitle
|
import no.nordicsemi.android.ui.view.SectionTitle
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ internal fun GLSContentView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Uni
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SettingsView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Unit) {
|
private fun SettingsView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Unit) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
SectionTitle(icon = Icons.Default.Settings, title = "Request items")
|
SectionTitle(icon = Icons.Default.Settings, title = "Request items")
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
@@ -118,7 +118,7 @@ private fun SettingsView(state: GLSData, onEvent: (GLSScreenViewEvent) -> Unit)
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RecordsView(state: GLSData) {
|
private fun RecordsView(state: GLSData) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
if (state.records.isEmpty()) {
|
if (state.records.isEmpty()) {
|
||||||
RecordsViewWithoutData()
|
RecordsViewWithoutData()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
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.R
|
||||||
import no.nordicsemi.android.gls.main.viewmodel.GLSViewModel
|
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.BackIconAppBar
|
||||||
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.ui.view.NavigateUpButton
|
||||||
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
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun GLSScreen() {
|
fun GLSScreen() {
|
||||||
@@ -62,18 +69,18 @@ fun GLSScreen() {
|
|||||||
|
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
when (state) {
|
when (state) {
|
||||||
NoDeviceState -> NoDeviceView()
|
NoDeviceState -> DeviceConnectingView()
|
||||||
is WorkingState -> when (state.result) {
|
is WorkingState -> when (state.result) {
|
||||||
is IdleResult,
|
is IdleResult,
|
||||||
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
|
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) }
|
||||||
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
|
||||||
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) }
|
||||||
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
||||||
is SuccessResult -> GLSContentView(state.result.data) { viewModel.onEvent(it) }
|
is SuccessResult -> GLSContentView(state.result.data) { viewModel.onEvent(it) }
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.gls.main.viewmodel
|
package no.nordicsemi.android.gls.main.viewmodel
|
||||||
|
|
||||||
|
import android.os.ParcelUuid
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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.AppAnalytics
|
||||||
import no.nordicsemi.android.analytics.Profile
|
import no.nordicsemi.android.analytics.Profile
|
||||||
import no.nordicsemi.android.analytics.ProfileConnectedEvent
|
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.GlsDetailsDestinationId
|
||||||
import no.nordicsemi.android.gls.data.GLS_SERVICE_UUID
|
import no.nordicsemi.android.gls.data.GLS_SERVICE_UUID
|
||||||
import no.nordicsemi.android.gls.main.view.*
|
import no.nordicsemi.android.gls.main.view.*
|
||||||
import no.nordicsemi.android.gls.repository.GLSRepository
|
import no.nordicsemi.android.gls.repository.GLSRepository
|
||||||
import no.nordicsemi.android.navigation.*
|
|
||||||
import no.nordicsemi.android.service.ConnectedResult
|
import no.nordicsemi.android.service.ConnectedResult
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
||||||
import no.nordicsemi.android.utils.getDevice
|
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import no.nordicsemi.ui.scanner.ScannerDestinationId
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class GLSViewModel @Inject constructor(
|
internal class GLSViewModel @Inject constructor(
|
||||||
private val repository: GLSRepository,
|
private val repository: GLSRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -64,20 +64,18 @@ internal class GLSViewModel @Inject constructor(
|
|||||||
val state = _state.asStateFlow()
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(GLS_SERVICE_UUID))
|
navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(GLS_SERVICE_UUID))
|
||||||
|
|
||||||
navigationManager.recentResult.onEach {
|
navigationManager.resultFrom(ScannerDestinationId)
|
||||||
if (it.destinationId == ScannerDestinationId) {
|
.onEach { handleResult(it) }
|
||||||
handleArgs(it)
|
.launchIn(viewModelScope)
|
||||||
}
|
|
||||||
}.launchIn(viewModelScope)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleArgs(args: DestinationResult) {
|
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) {
|
||||||
when (args) {
|
when (result) {
|
||||||
is CancelDestinationResult -> navigationManager.navigateUp()
|
is NavigationResult.Cancelled -> navigationManager.navigateUp()
|
||||||
is SuccessDestinationResult -> connectDevice(args.getDevice())
|
is NavigationResult.Success -> connectDevice(result.value)
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEvent(event: GLSScreenViewEvent) {
|
fun onEvent(event: GLSScreenViewEvent) {
|
||||||
@@ -85,9 +83,9 @@ internal class GLSViewModel @Inject constructor(
|
|||||||
OpenLoggerEvent -> repository.openLogger()
|
OpenLoggerEvent -> repository.openLogger()
|
||||||
DisconnectEvent -> navigationManager.navigateUp()
|
DisconnectEvent -> navigationManager.navigateUp()
|
||||||
is OnWorkingModeSelected -> repository.requestMode(event.workingMode)
|
is OnWorkingModeSelected -> repository.requestMode(event.workingMode)
|
||||||
is OnGLSRecordClick -> navigationManager.navigateTo(GlsDetailsDestinationId, AnyArgument(event.record))
|
is OnGLSRecordClick -> navigationManager.navigateTo(GlsDetailsDestinationId, event.record)
|
||||||
DisconnectEvent -> navigationManager.navigateUp()
|
DisconnectEvent -> navigationManager.navigateUp()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun connectDevice(device: DiscoveredBluetoothDevice) {
|
private fun connectDevice(device: DiscoveredBluetoothDevice) {
|
||||||
|
|||||||
@@ -42,15 +42,14 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
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.GLSData
|
||||||
import no.nordicsemi.android.gls.data.GLSManager
|
import no.nordicsemi.android.gls.data.GLSManager
|
||||||
import no.nordicsemi.android.gls.data.WorkingMode
|
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.service.BleManagerResult
|
||||||
import no.nordicsemi.android.ui.view.StringConst
|
import no.nordicsemi.android.ui.view.StringConst
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ViewModelScoped
|
@ViewModelScoped
|
||||||
@@ -65,7 +64,7 @@ internal class GLSRepository @Inject constructor(
|
|||||||
private var logger: NordicLogger? = null
|
private var logger: NordicLogger? = null
|
||||||
|
|
||||||
fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow<BleManagerResult<GLSData>> = callbackFlow {
|
fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow<BleManagerResult<GLSData>> = 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
|
logger = it
|
||||||
}
|
}
|
||||||
val managerInstance = manager ?: GLSManager(context, scope, createdLogger)
|
val managerInstance = manager ?: GLSManager(context, scope, createdLogger)
|
||||||
@@ -100,7 +99,7 @@ internal class GLSRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
logger?.openLogger()
|
NordicLogger.launch(context, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestMode(workingMode: WorkingMode) {
|
fun requestMode(workingMode: WorkingMode) {
|
||||||
@@ -108,6 +107,6 @@ internal class GLSRepository @Inject constructor(
|
|||||||
WorkingMode.ALL -> manager?.requestAllRecords()
|
WorkingMode.ALL -> manager?.requestAllRecords()
|
||||||
WorkingMode.LAST -> manager?.requestLastRecord()
|
WorkingMode.LAST -> manager?.requestLastRecord()
|
||||||
WorkingMode.FIRST -> manager?.requestFirstRecord()
|
WorkingMode.FIRST -> manager?.requestFirstRecord()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":lib_analytics"))
|
implementation(project(":lib_analytics"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ dependencies {
|
|||||||
implementation(libs.nordic.uiscanner)
|
implementation(libs.nordic.uiscanner)
|
||||||
implementation(libs.nordic.uilogger)
|
implementation(libs.nordic.uilogger)
|
||||||
|
|
||||||
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
implementation(libs.androidx.compose.material.iconsExtended)
|
implementation(libs.androidx.compose.material.iconsExtended)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
|
|||||||
@@ -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.common.callback.hr.HeartRateMeasurementResponse
|
||||||
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
||||||
import no.nordicsemi.android.ble.ktx.suspendForValidResponse
|
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.service.ConnectionObserverAdapter
|
||||||
import no.nordicsemi.android.utils.launchWithCatch
|
import no.nordicsemi.android.utils.launchWithCatch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|||||||
@@ -34,18 +34,22 @@ package no.nordicsemi.android.hrs.service
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
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 kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
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.HRSData
|
||||||
import no.nordicsemi.android.hrs.data.HRSManager
|
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.BleManagerResult
|
||||||
import no.nordicsemi.android.service.IdleResult
|
import no.nordicsemi.android.service.IdleResult
|
||||||
import no.nordicsemi.android.service.ServiceManager
|
import no.nordicsemi.android.service.ServiceManager
|
||||||
import no.nordicsemi.android.ui.view.StringConst
|
import no.nordicsemi.android.ui.view.StringConst
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -71,7 +75,7 @@ class HRSRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) {
|
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
|
logger = it
|
||||||
}
|
}
|
||||||
val manager = HRSManager(context, scope, createdLogger)
|
val manager = HRSManager(context, scope, createdLogger)
|
||||||
@@ -87,7 +91,7 @@ class HRSRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
logger?.openLogger()
|
NordicLogger.launch(context, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun HRSManager.start(device: DiscoveredBluetoothDevice) {
|
private suspend fun HRSManager.start(device: DiscoveredBluetoothDevice) {
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
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.DEVICE_DATA
|
||||||
import no.nordicsemi.android.service.NotificationService
|
import no.nordicsemi.android.service.NotificationService
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -48,7 +49,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.hrs.R
|
import no.nordicsemi.android.hrs.R
|
||||||
import no.nordicsemi.android.hrs.data.HRSData
|
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.BatteryLevelView
|
||||||
import no.nordicsemi.android.ui.view.SectionTitle
|
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)
|
modifier = Modifier.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
SectionTitle(
|
SectionTitle(
|
||||||
resId = R.drawable.ic_chart_line,
|
resId = R.drawable.ic_chart_line,
|
||||||
title = stringResource(id = R.string.hrs_section_data),
|
title = stringResource(id = R.string.hrs_section_data),
|
||||||
|
|||||||
@@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
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.R
|
||||||
import no.nordicsemi.android.hrs.viewmodel.HRSViewModel
|
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.BackIconAppBar
|
||||||
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.ui.view.NavigateUpButton
|
||||||
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
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HRSScreen() {
|
fun HRSScreen() {
|
||||||
@@ -62,18 +69,18 @@ fun HRSScreen() {
|
|||||||
|
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
when (state) {
|
when (state) {
|
||||||
NoDeviceState -> NoDeviceView()
|
NoDeviceState -> DeviceConnectingView()
|
||||||
is WorkingState -> when (state.result) {
|
is WorkingState -> when (state.result) {
|
||||||
is IdleResult,
|
is IdleResult,
|
||||||
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
|
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) }
|
||||||
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
|
||||||
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) }
|
||||||
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
||||||
is SuccessResult -> HRSContentView(state.result.data, state.zoomIn) { viewModel.onEvent(it) }
|
is SuccessResult -> HRSContentView(state.result.data, state.zoomIn) { viewModel.onEvent(it) }
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,28 +31,40 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.hrs.viewmodel
|
package no.nordicsemi.android.hrs.viewmodel
|
||||||
|
|
||||||
|
import android.os.ParcelUuid
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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 kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.analytics.AppAnalytics
|
import no.nordicsemi.android.analytics.AppAnalytics
|
||||||
import no.nordicsemi.android.analytics.Profile
|
import no.nordicsemi.android.analytics.Profile
|
||||||
import no.nordicsemi.android.analytics.ProfileConnectedEvent
|
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.data.HRS_SERVICE_UUID
|
||||||
import no.nordicsemi.android.hrs.service.HRSRepository
|
import no.nordicsemi.android.hrs.service.HRSRepository
|
||||||
import no.nordicsemi.android.hrs.view.*
|
import no.nordicsemi.android.hrs.view.DisconnectEvent
|
||||||
import no.nordicsemi.android.navigation.*
|
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.service.ConnectedResult
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
||||||
import no.nordicsemi.android.utils.getDevice
|
|
||||||
import no.nordicsemi.ui.scanner.ScannerDestinationId
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class HRSViewModel @Inject constructor(
|
internal class HRSViewModel @Inject constructor(
|
||||||
private val repository: HRSRepository,
|
private val repository: HRSRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -77,20 +89,18 @@ internal class HRSViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun requestBluetoothDevice() {
|
private fun requestBluetoothDevice() {
|
||||||
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(HRS_SERVICE_UUID))
|
navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(HRS_SERVICE_UUID))
|
||||||
|
|
||||||
navigationManager.recentResult.onEach {
|
navigationManager.resultFrom(ScannerDestinationId)
|
||||||
if (it.destinationId == ScannerDestinationId) {
|
.onEach { handleResult(it) }
|
||||||
handleArgs(it)
|
.launchIn(viewModelScope)
|
||||||
}
|
|
||||||
}.launchIn(viewModelScope)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleArgs(args: DestinationResult) {
|
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) {
|
||||||
when (args) {
|
when (result) {
|
||||||
is CancelDestinationResult -> navigationManager.navigateUp()
|
is NavigationResult.Cancelled -> navigationManager.navigateUp()
|
||||||
is SuccessDestinationResult -> repository.launch(args.getDevice())
|
is NavigationResult.Success -> repository.launch(result.value)
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEvent(event: HRSScreenViewEvent) {
|
fun onEvent(event: HRSScreenViewEvent) {
|
||||||
@@ -99,7 +109,7 @@ internal class HRSViewModel @Inject constructor(
|
|||||||
NavigateUpEvent -> navigationManager.navigateUp()
|
NavigateUpEvent -> navigationManager.navigateUp()
|
||||||
OpenLoggerEvent -> repository.openLogger()
|
OpenLoggerEvent -> repository.openLogger()
|
||||||
SwitchZoomEvent -> onZoomButtonClicked()
|
SwitchZoomEvent -> onZoomButtonClicked()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onZoomButtonClicked() {
|
private fun onZoomButtonClicked() {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":lib_analytics"))
|
implementation(project(":lib_analytics"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ dependencies {
|
|||||||
implementation(libs.nordic.navigation)
|
implementation(libs.nordic.navigation)
|
||||||
implementation(libs.nordic.uilogger)
|
implementation(libs.nordic.uilogger)
|
||||||
|
|
||||||
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
implementation(libs.androidx.compose.material.iconsExtended)
|
implementation(libs.androidx.compose.material.iconsExtended)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
|
|||||||
@@ -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.battery.BatteryLevelResponse
|
||||||
import no.nordicsemi.android.ble.common.callback.ht.TemperatureMeasurementResponse
|
import no.nordicsemi.android.ble.common.callback.ht.TemperatureMeasurementResponse
|
||||||
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
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 no.nordicsemi.android.service.ConnectionObserverAdapter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|||||||
@@ -34,18 +34,22 @@ package no.nordicsemi.android.hts.repository
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
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 kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
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.HTSData
|
||||||
import no.nordicsemi.android.hts.data.HTSManager
|
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.BleManagerResult
|
||||||
import no.nordicsemi.android.service.IdleResult
|
import no.nordicsemi.android.service.IdleResult
|
||||||
import no.nordicsemi.android.service.ServiceManager
|
import no.nordicsemi.android.service.ServiceManager
|
||||||
import no.nordicsemi.android.ui.view.StringConst
|
import no.nordicsemi.android.ui.view.StringConst
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -71,7 +75,7 @@ class HTSRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) {
|
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
|
logger = it
|
||||||
}
|
}
|
||||||
val manager = HTSManager(context, scope, createdLogger)
|
val manager = HTSManager(context, scope, createdLogger)
|
||||||
@@ -87,7 +91,7 @@ class HTSRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
logger?.openLogger()
|
NordicLogger.launch(context, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun HTSManager.start(device: DiscoveredBluetoothDevice) {
|
private suspend fun HTSManager.start(device: DiscoveredBluetoothDevice) {
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
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.DEVICE_DATA
|
||||||
import no.nordicsemi.android.service.NotificationService
|
import no.nordicsemi.android.service.NotificationService
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ package no.nordicsemi.android.hts.view
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -40,10 +41,9 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
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.R
|
||||||
import no.nordicsemi.android.hts.data.HTSData
|
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.BatteryLevelView
|
||||||
import no.nordicsemi.android.ui.view.KeyValueField
|
import no.nordicsemi.android.ui.view.KeyValueField
|
||||||
import no.nordicsemi.android.ui.view.SectionTitle
|
import no.nordicsemi.android.ui.view.SectionTitle
|
||||||
@@ -56,7 +56,7 @@ internal fun HTSContentView(state: HTSData, temperatureUnit: TemperatureUnit, on
|
|||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
SectionTitle(resId = R.drawable.ic_thermometer, title = "Settings")
|
SectionTitle(resId = R.drawable.ic_thermometer, title = "Settings")
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
@@ -68,7 +68,7 @@ internal fun HTSContentView(state: HTSData, temperatureUnit: TemperatureUnit, on
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.hts_records_section))
|
SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.hts_records_section))
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|||||||
@@ -31,8 +31,8 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.hts.view
|
package no.nordicsemi.android.hts.view
|
||||||
|
|
||||||
import no.nordicsemi.android.theme.RadioButtonItem
|
import no.nordicsemi.android.common.theme.view.RadioButtonItem
|
||||||
import no.nordicsemi.android.theme.RadioGroupViewEntity
|
import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity
|
||||||
|
|
||||||
private const val DISPLAY_FAHRENHEIT = "°F"
|
private const val DISPLAY_FAHRENHEIT = "°F"
|
||||||
private const val DISPLAY_CELSIUS = "°C"
|
private const val DISPLAY_CELSIUS = "°C"
|
||||||
|
|||||||
@@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
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.R
|
||||||
import no.nordicsemi.android.hts.viewmodel.HTSViewModel
|
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.BackIconAppBar
|
||||||
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.ui.view.NavigateUpButton
|
||||||
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
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HTSScreen() {
|
fun HTSScreen() {
|
||||||
@@ -62,18 +69,18 @@ fun HTSScreen() {
|
|||||||
|
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
when (state.htsManagerState) {
|
when (state.htsManagerState) {
|
||||||
NoDeviceState -> NoDeviceView()
|
NoDeviceState -> DeviceConnectingView()
|
||||||
is WorkingState -> when (state.htsManagerState.result) {
|
is WorkingState -> when (state.htsManagerState.result) {
|
||||||
is IdleResult,
|
is IdleResult,
|
||||||
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
|
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) }
|
||||||
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
|
||||||
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) }
|
||||||
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
||||||
is SuccessResult -> HTSContentView(state.htsManagerState.result.data, state.temperatureUnit) { viewModel.onEvent(it) }
|
is SuccessResult -> HTSContentView(state.htsManagerState.result.data, state.temperatureUnit) { viewModel.onEvent(it) }
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,28 +31,39 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.hts.viewmodel
|
package no.nordicsemi.android.hts.viewmodel
|
||||||
|
|
||||||
|
import android.os.ParcelUuid
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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 kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.analytics.AppAnalytics
|
import no.nordicsemi.android.analytics.AppAnalytics
|
||||||
import no.nordicsemi.android.analytics.Profile
|
import no.nordicsemi.android.analytics.Profile
|
||||||
import no.nordicsemi.android.analytics.ProfileConnectedEvent
|
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.data.HTS_SERVICE_UUID
|
||||||
import no.nordicsemi.android.hts.repository.HTSRepository
|
import no.nordicsemi.android.hts.repository.HTSRepository
|
||||||
import no.nordicsemi.android.hts.view.*
|
import no.nordicsemi.android.hts.view.DisconnectEvent
|
||||||
import no.nordicsemi.android.navigation.*
|
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.service.ConnectedResult
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
||||||
import no.nordicsemi.android.utils.getDevice
|
|
||||||
import no.nordicsemi.ui.scanner.ScannerDestinationId
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class HTSViewModel @Inject constructor(
|
internal class HTSViewModel @Inject constructor(
|
||||||
private val repository: HTSRepository,
|
private val repository: HTSRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -76,20 +87,18 @@ internal class HTSViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun requestBluetoothDevice() {
|
private fun requestBluetoothDevice() {
|
||||||
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(HTS_SERVICE_UUID))
|
navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(HTS_SERVICE_UUID))
|
||||||
|
|
||||||
navigationManager.recentResult.onEach {
|
navigationManager.resultFrom(ScannerDestinationId)
|
||||||
if (it.destinationId == ScannerDestinationId) {
|
.onEach { handleResult(it) }
|
||||||
handleArgs(it)
|
.launchIn(viewModelScope)
|
||||||
}
|
|
||||||
}.launchIn(viewModelScope)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleArgs(args: DestinationResult) {
|
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) {
|
||||||
when (args) {
|
when (result) {
|
||||||
is CancelDestinationResult -> navigationManager.navigateUp()
|
is NavigationResult.Cancelled -> navigationManager.navigateUp()
|
||||||
is SuccessDestinationResult -> repository.launch(args.getDevice())
|
is NavigationResult.Success -> repository.launch(result.value)
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEvent(event: HTSScreenViewEvent) {
|
fun onEvent(event: HTSScreenViewEvent) {
|
||||||
@@ -98,7 +107,7 @@ internal class HTSViewModel @Inject constructor(
|
|||||||
is OnTemperatureUnitSelected -> onTemperatureUnitSelected(event)
|
is OnTemperatureUnitSelected -> onTemperatureUnitSelected(event)
|
||||||
NavigateUp -> navigationManager.navigateUp()
|
NavigateUp -> navigationManager.navigateUp()
|
||||||
OpenLoggerEvent -> repository.openLogger()
|
OpenLoggerEvent -> repository.openLogger()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun disconnect() {
|
private fun disconnect() {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":lib_analytics"))
|
implementation(project(":lib_analytics"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ dependencies {
|
|||||||
implementation(libs.nordic.navigation)
|
implementation(libs.nordic.navigation)
|
||||||
implementation(libs.nordic.uilogger)
|
implementation(libs.nordic.uilogger)
|
||||||
|
|
||||||
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
implementation(libs.androidx.compose.material.iconsExtended)
|
implementation(libs.androidx.compose.material.iconsExtended)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
|
|||||||
@@ -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.common.data.alert.AlertLevelData
|
||||||
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
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.service.ConnectionObserverAdapter
|
||||||
import no.nordicsemi.android.utils.launchWithCatch
|
import no.nordicsemi.android.utils.launchWithCatch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|||||||
@@ -35,15 +35,15 @@ import android.content.Context
|
|||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import no.nordicsemi.android.logger.NordicLogger
|
import no.nordicsemi.android.common.logger.NordicLogger
|
||||||
import no.nordicsemi.android.logger.NordicLoggerFactory
|
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.AlarmLevel
|
||||||
import no.nordicsemi.android.prx.data.PRXData
|
import no.nordicsemi.android.prx.data.PRXData
|
||||||
import no.nordicsemi.android.prx.data.PRXManager
|
import no.nordicsemi.android.prx.data.PRXManager
|
||||||
import no.nordicsemi.android.prx.data.ProximityServerManager
|
import no.nordicsemi.android.prx.data.ProximityServerManager
|
||||||
import no.nordicsemi.android.service.*
|
import no.nordicsemi.android.service.*
|
||||||
import no.nordicsemi.android.ui.view.StringConst
|
import no.nordicsemi.android.ui.view.StringConst
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ class PRXRepository @Inject internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) {
|
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
|
logger = it
|
||||||
}
|
}
|
||||||
val manager = PRXManager(context, scope, createdLogger)
|
val manager = PRXManager(context, scope, createdLogger)
|
||||||
@@ -114,7 +114,7 @@ class PRXRepository @Inject internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
logger?.openLogger()
|
NordicLogger.launch(context, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun release() {
|
fun release() {
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
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.DEVICE_DATA
|
||||||
import no.nordicsemi.android.service.NotificationService
|
import no.nordicsemi.android.service.NotificationService
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.theme.ScreenSection
|
|
||||||
import no.nordicsemi.android.prx.R
|
import no.nordicsemi.android.prx.R
|
||||||
import no.nordicsemi.android.prx.data.PRXData
|
import no.nordicsemi.android.prx.data.PRXData
|
||||||
import no.nordicsemi.android.ui.view.BatteryLevelView
|
import no.nordicsemi.android.ui.view.BatteryLevelView
|
||||||
@@ -81,7 +81,7 @@ internal fun ContentView(state: PRXData, onEvent: (PRXScreenViewEvent) -> Unit)
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SettingsSection(state: PRXData, onEvent: (PRXScreenViewEvent) -> Unit) {
|
private fun SettingsSection(state: PRXData, onEvent: (PRXScreenViewEvent) -> Unit) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
SectionTitle(icon = Icons.Default.Settings, title = stringResource(R.string.prx_settings))
|
SectionTitle(icon = Icons.Default.Settings, title = stringResource(R.string.prx_settings))
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
@@ -114,7 +114,7 @@ private fun TurnAlarmOffButton(onEvent: (PRXScreenViewEvent) -> Unit) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RecordsSection(state: PRXData) {
|
private fun RecordsSection(state: PRXData) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.prx_records))
|
SectionTitle(resId = R.drawable.ic_records, title = stringResource(id = R.string.prx_records))
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import androidx.compose.material.icons.filled.HighlightOff
|
|||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -46,7 +47,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.theme.ScreenSection
|
|
||||||
import no.nordicsemi.android.prx.R
|
import no.nordicsemi.android.prx.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -57,7 +57,7 @@ fun DeviceOutOfRangeView(navigateUp: () -> Unit) {
|
|||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.HighlightOff,
|
imageVector = Icons.Default.HighlightOff,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
|||||||
@@ -40,16 +40,23 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
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.R
|
||||||
import no.nordicsemi.android.prx.viewmodel.PRXViewModel
|
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.BackIconAppBar
|
||||||
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.ui.view.NavigateUpButton
|
||||||
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
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PRXScreen() {
|
fun PRXScreen() {
|
||||||
@@ -63,18 +70,18 @@ fun PRXScreen() {
|
|||||||
|
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
when (state) {
|
when (state) {
|
||||||
NoDeviceState -> NoDeviceView()
|
NoDeviceState -> DeviceConnectingView()
|
||||||
is WorkingState -> when (state.result) {
|
is WorkingState -> when (state.result) {
|
||||||
is IdleResult,
|
is IdleResult,
|
||||||
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
|
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) }
|
||||||
is LinkLossResult -> DeviceOutOfRangeView { viewModel.onEvent(DisconnectEvent) }
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
|
||||||
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) }
|
||||||
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
||||||
is SuccessResult -> ContentView(state.result.data) { viewModel.onEvent(it) }
|
is SuccessResult -> ContentView(state.result.data) { viewModel.onEvent(it) }
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.prx.viewmodel
|
package no.nordicsemi.android.prx.viewmodel
|
||||||
|
|
||||||
|
import android.os.ParcelUuid
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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.AppAnalytics
|
||||||
import no.nordicsemi.android.analytics.Profile
|
import no.nordicsemi.android.analytics.Profile
|
||||||
import no.nordicsemi.android.analytics.ProfileConnectedEvent
|
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.data.PRX_SERVICE_UUID
|
||||||
import no.nordicsemi.android.prx.repository.PRXRepository
|
import no.nordicsemi.android.prx.repository.PRXRepository
|
||||||
import no.nordicsemi.android.prx.view.*
|
import no.nordicsemi.android.prx.view.*
|
||||||
import no.nordicsemi.android.service.ConnectedResult
|
import no.nordicsemi.android.service.ConnectedResult
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
||||||
import no.nordicsemi.android.utils.getDevice
|
|
||||||
import no.nordicsemi.ui.scanner.ScannerDestinationId
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class PRXViewModel @Inject constructor(
|
internal class PRXViewModel @Inject constructor(
|
||||||
private val repository: PRXRepository,
|
private val repository: PRXRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -76,20 +77,18 @@ internal class PRXViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun requestBluetoothDevice() {
|
private fun requestBluetoothDevice() {
|
||||||
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(PRX_SERVICE_UUID))
|
navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(PRX_SERVICE_UUID))
|
||||||
|
|
||||||
navigationManager.recentResult.onEach {
|
navigationManager.resultFrom(ScannerDestinationId)
|
||||||
if (it.destinationId == ScannerDestinationId) {
|
.onEach { handleResult(it) }
|
||||||
handleArgs(it)
|
.launchIn(viewModelScope)
|
||||||
}
|
|
||||||
}.launchIn(viewModelScope)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleArgs(args: DestinationResult) {
|
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) {
|
||||||
when (args) {
|
when (result) {
|
||||||
is CancelDestinationResult -> navigationManager.navigateUp()
|
is NavigationResult.Cancelled -> navigationManager.navigateUp()
|
||||||
is SuccessDestinationResult -> repository.launch(args.getDevice())
|
is NavigationResult.Success -> repository.launch(result.value)
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEvent(event: PRXScreenViewEvent) {
|
fun onEvent(event: PRXScreenViewEvent) {
|
||||||
@@ -99,7 +98,7 @@ internal class PRXViewModel @Inject constructor(
|
|||||||
TurnOnAlert -> repository.enableAlarm()
|
TurnOnAlert -> repository.enableAlarm()
|
||||||
NavigateUpEvent -> navigationManager.navigateUp()
|
NavigateUpEvent -> navigationManager.navigateUp()
|
||||||
OpenLoggerEvent -> repository.openLogger()
|
OpenLoggerEvent -> repository.openLogger()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun disconnect() {
|
private fun disconnect() {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":lib_analytics"))
|
implementation(project(":lib_analytics"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ dependencies {
|
|||||||
implementation(libs.nordic.navigation)
|
implementation(libs.nordic.navigation)
|
||||||
implementation(libs.nordic.uilogger)
|
implementation(libs.nordic.uilogger)
|
||||||
|
|
||||||
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
implementation(libs.androidx.compose.material.iconsExtended)
|
implementation(libs.androidx.compose.material.iconsExtended)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
|
|||||||
@@ -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.battery.BatteryLevelResponse
|
||||||
import no.nordicsemi.android.ble.common.callback.rsc.RunningSpeedAndCadenceMeasurementResponse
|
import no.nordicsemi.android.ble.common.callback.rsc.RunningSpeedAndCadenceMeasurementResponse
|
||||||
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
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 no.nordicsemi.android.service.ConnectionObserverAdapter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|||||||
@@ -34,18 +34,22 @@ package no.nordicsemi.android.rscs.repository
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
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 kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
import no.nordicsemi.android.ble.ktx.suspend
|
||||||
import no.nordicsemi.android.logger.NordicLogger
|
import no.nordicsemi.android.common.logger.NordicLogger
|
||||||
import no.nordicsemi.android.logger.NordicLoggerFactory
|
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.RSCSData
|
||||||
import no.nordicsemi.android.rscs.data.RSCSManager
|
import no.nordicsemi.android.rscs.data.RSCSManager
|
||||||
import no.nordicsemi.android.service.BleManagerResult
|
import no.nordicsemi.android.service.BleManagerResult
|
||||||
import no.nordicsemi.android.service.IdleResult
|
import no.nordicsemi.android.service.IdleResult
|
||||||
import no.nordicsemi.android.service.ServiceManager
|
import no.nordicsemi.android.service.ServiceManager
|
||||||
import no.nordicsemi.android.ui.view.StringConst
|
import no.nordicsemi.android.ui.view.StringConst
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -71,7 +75,7 @@ class RSCSRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) {
|
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
|
logger = it
|
||||||
}
|
}
|
||||||
val manager = RSCSManager(context, scope, createdLogger)
|
val manager = RSCSManager(context, scope, createdLogger)
|
||||||
@@ -87,7 +91,7 @@ class RSCSRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
logger?.openLogger()
|
NordicLogger.launch(context, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun RSCSManager.start(device: DiscoveredBluetoothDevice) {
|
private suspend fun RSCSManager.start(device: DiscoveredBluetoothDevice) {
|
||||||
|
|||||||
@@ -39,16 +39,23 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
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.R
|
||||||
import no.nordicsemi.android.rscs.viewmodel.RSCSViewModel
|
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.BackIconAppBar
|
||||||
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.ui.view.NavigateUpButton
|
||||||
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
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RSCSScreen() {
|
fun RSCSScreen() {
|
||||||
@@ -62,18 +69,18 @@ fun RSCSScreen() {
|
|||||||
|
|
||||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
when (state) {
|
when (state) {
|
||||||
NoDeviceState -> NoDeviceView()
|
NoDeviceState -> DeviceConnectingView()
|
||||||
is WorkingState -> when (state.result) {
|
is WorkingState -> when (state.result) {
|
||||||
is IdleResult,
|
is IdleResult,
|
||||||
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER, navigateUp)
|
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) }
|
||||||
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp)
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
|
||||||
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp)
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) }
|
||||||
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN, navigateUp)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
||||||
is SuccessResult -> RSCSContentView(state.result.data) { viewModel.onEvent(it) }
|
is SuccessResult -> RSCSContentView(state.result.data) { viewModel.onEvent(it) }
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ package no.nordicsemi.android.rscs.view
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material3.OutlinedCard
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.theme.ScreenSection
|
|
||||||
import no.nordicsemi.android.rscs.R
|
import no.nordicsemi.android.rscs.R
|
||||||
import no.nordicsemi.android.rscs.data.RSCSData
|
import no.nordicsemi.android.rscs.data.RSCSData
|
||||||
import no.nordicsemi.android.ui.view.KeyValueField
|
import no.nordicsemi.android.ui.view.KeyValueField
|
||||||
@@ -46,7 +46,7 @@ import no.nordicsemi.android.ui.view.SectionTitle
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SensorsReadingView(state: RSCSData) {
|
internal fun SensorsReadingView(state: RSCSData) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
SectionTitle(resId = R.drawable.ic_records, title = "Records")
|
SectionTitle(resId = R.drawable.ic_records, title = "Records")
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|||||||
@@ -31,28 +31,39 @@
|
|||||||
|
|
||||||
package no.nordicsemi.android.rscs.viewmodel
|
package no.nordicsemi.android.rscs.viewmodel
|
||||||
|
|
||||||
|
import android.os.ParcelUuid
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
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 kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.analytics.AppAnalytics
|
import no.nordicsemi.android.analytics.AppAnalytics
|
||||||
import no.nordicsemi.android.analytics.Profile
|
import no.nordicsemi.android.analytics.Profile
|
||||||
import no.nordicsemi.android.analytics.ProfileConnectedEvent
|
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.data.RSCS_SERVICE_UUID
|
||||||
import no.nordicsemi.android.rscs.repository.RSCSRepository
|
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.service.ConnectedResult
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
||||||
import no.nordicsemi.android.utils.getDevice
|
|
||||||
import no.nordicsemi.ui.scanner.ScannerDestinationId
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class RSCSViewModel @Inject constructor(
|
internal class RSCSViewModel @Inject constructor(
|
||||||
private val repository: RSCSRepository,
|
private val repository: RSCSRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: Navigator,
|
||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
@@ -76,20 +87,18 @@ internal class RSCSViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun requestBluetoothDevice() {
|
private fun requestBluetoothDevice() {
|
||||||
navigationManager.navigateTo(ScannerDestinationId, UUIDArgument(RSCS_SERVICE_UUID))
|
navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(RSCS_SERVICE_UUID))
|
||||||
|
|
||||||
navigationManager.recentResult.onEach {
|
navigationManager.resultFrom(ScannerDestinationId)
|
||||||
if (it.destinationId == ScannerDestinationId) {
|
.onEach { handleResult(it) }
|
||||||
handleArgs(it)
|
.launchIn(viewModelScope)
|
||||||
}
|
|
||||||
}.launchIn(viewModelScope)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleArgs(args: DestinationResult) {
|
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) {
|
||||||
when (args) {
|
when (result) {
|
||||||
is CancelDestinationResult -> navigationManager.navigateUp()
|
is NavigationResult.Cancelled -> navigationManager.navigateUp()
|
||||||
is SuccessDestinationResult -> repository.launch(args.getDevice())
|
is NavigationResult.Success -> repository.launch(result.value)
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onEvent(event: RSCScreenViewEvent) {
|
fun onEvent(event: RSCScreenViewEvent) {
|
||||||
@@ -97,7 +106,7 @@ internal class RSCSViewModel @Inject constructor(
|
|||||||
DisconnectEvent -> disconnect()
|
DisconnectEvent -> disconnect()
|
||||||
NavigateUpEvent -> navigationManager.navigateUp()
|
NavigateUpEvent -> navigationManager.navigateUp()
|
||||||
OpenLoggerEvent -> repository.openLogger()
|
OpenLoggerEvent -> repository.openLogger()
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun disconnect() {
|
private fun disconnect() {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.nordic.library)
|
alias(libs.plugins.nordic.feature)
|
||||||
alias(libs.plugins.kotlin.serialization)
|
alias(libs.plugins.kotlin.serialization)
|
||||||
// id("com.google.protobuf")
|
// id("com.google.protobuf")
|
||||||
alias(libs.plugins.kotlin.kapt)
|
alias(libs.plugins.kotlin.kapt)
|
||||||
@@ -49,6 +49,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":lib_analytics"))
|
implementation(project(":lib_analytics"))
|
||||||
implementation(project(":lib_service"))
|
implementation(project(":lib_service"))
|
||||||
|
implementation(project(":lib_scanner"))
|
||||||
implementation(project(":lib_ui"))
|
implementation(project(":lib_ui"))
|
||||||
implementation(project(":lib_utils"))
|
implementation(project(":lib_utils"))
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ dependencies {
|
|||||||
implementation(libs.room.ktx)
|
implementation(libs.room.ktx)
|
||||||
|
|
||||||
// kapt(libs.room.compiler)
|
// kapt(libs.room.compiler)
|
||||||
// kapt("androidx.room:room-compiler:2.5.0")
|
kapt(libs.room.compiler)
|
||||||
// kapt("")
|
// kapt("")
|
||||||
|
|
||||||
implementation(libs.nordic.ble.common)
|
implementation(libs.nordic.ble.common)
|
||||||
@@ -67,6 +68,10 @@ dependencies {
|
|||||||
implementation(libs.nordic.navigation)
|
implementation(libs.nordic.navigation)
|
||||||
implementation(libs.nordic.uilogger)
|
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.compose.material.iconsExtended)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.compose.material3)
|
implementation(libs.androidx.compose.material3)
|
||||||
|
|||||||
@@ -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.asFlow
|
||||||
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
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.service.ConnectionObserverAdapter
|
||||||
import no.nordicsemi.android.utils.EMPTY
|
import no.nordicsemi.android.utils.EMPTY
|
||||||
import no.nordicsemi.android.utils.launchWithCatch
|
import no.nordicsemi.android.utils.launchWithCatch
|
||||||
|
|||||||
@@ -28,57 +28,41 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* 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.Attribute;
|
import org.simpleframework.xml.Root
|
||||||
import org.simpleframework.xml.ElementArray;
|
import org.simpleframework.xml.core.PersistenceException
|
||||||
import org.simpleframework.xml.Root;
|
import org.simpleframework.xml.core.Validate
|
||||||
import org.simpleframework.xml.core.PersistenceException;
|
|
||||||
import org.simpleframework.xml.core.Validate;
|
|
||||||
|
|
||||||
@Root
|
@Root
|
||||||
public class XmlConfiguration {
|
class XmlConfiguration {
|
||||||
public static final int COMMANDS_COUNT = 9;
|
/**
|
||||||
|
* 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<XmlMacro?> = 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
|
companion object {
|
||||||
private XmlMacro[] commands = new XmlMacro[COMMANDS_COUNT];
|
const val COMMANDS_COUNT = 9
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
* 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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -34,18 +34,27 @@ package no.nordicsemi.android.uart.repository
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineScope
|
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 kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.ble.ktx.suspend
|
import no.nordicsemi.android.ble.ktx.suspend
|
||||||
import no.nordicsemi.android.logger.NordicLogger
|
import no.nordicsemi.android.common.logger.NordicLogger
|
||||||
import no.nordicsemi.android.logger.NordicLoggerFactory
|
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.BleManagerResult
|
||||||
import no.nordicsemi.android.service.IdleResult
|
import no.nordicsemi.android.service.IdleResult
|
||||||
import no.nordicsemi.android.service.ServiceManager
|
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.ui.view.StringConst
|
||||||
import no.nordicsemi.android.uart.data.*
|
|
||||||
import no.nordicsemi.android.utils.EMPTY
|
import no.nordicsemi.android.utils.EMPTY
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -74,7 +83,7 @@ class UARTRepository @Inject internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) {
|
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
|
logger = it
|
||||||
}
|
}
|
||||||
val manager = UARTManager(context, scope, createdLogger)
|
val manager = UARTManager(context, scope, createdLogger)
|
||||||
@@ -103,7 +112,7 @@ class UARTRepository @Inject internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openLogger() {
|
fun openLogger() {
|
||||||
logger?.openLogger()
|
NordicLogger.launch(context, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun saveConfigurationName(name: String) {
|
suspend fun saveConfigurationName(name: String) {
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
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.DEVICE_DATA
|
||||||
import no.nordicsemi.android.service.NotificationService
|
import no.nordicsemi.android.service.NotificationService
|
||||||
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
|||||||
@@ -45,15 +45,15 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import no.nordicsemi.android.theme.RadioButtonGroup
|
import no.nordicsemi.android.common.theme.view.RadioButtonGroup
|
||||||
import no.nordicsemi.android.theme.RadioButtonItem
|
import no.nordicsemi.android.common.theme.view.RadioButtonItem
|
||||||
import no.nordicsemi.android.theme.RadioGroupViewEntity
|
import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity
|
||||||
import no.nordicsemi.android.theme.ScreenSection
|
|
||||||
import no.nordicsemi.android.ui.view.SectionTitle
|
import no.nordicsemi.android.ui.view.SectionTitle
|
||||||
import no.nordicsemi.android.uart.R
|
import no.nordicsemi.android.uart.R
|
||||||
import no.nordicsemi.android.uart.data.MacroEol
|
import no.nordicsemi.android.uart.data.MacroEol
|
||||||
import no.nordicsemi.android.utils.EMPTY
|
import no.nordicsemi.android.utils.EMPTY
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
internal fun InputSection(onEvent: (UARTViewEvent) -> Unit) {
|
internal fun InputSection(onEvent: (UARTViewEvent) -> Unit) {
|
||||||
val text = rememberSaveable { mutableStateOf(String.EMPTY) }
|
val text = rememberSaveable { mutableStateOf(String.EMPTY) }
|
||||||
@@ -105,7 +105,7 @@ internal fun EditInputSection(onEvent: (UARTViewEvent) -> Unit) {
|
|||||||
}
|
}
|
||||||
val viewEntity = RadioGroupViewEntity(items)
|
val viewEntity = RadioGroupViewEntity(items)
|
||||||
|
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.theme.ScreenSection
|
|
||||||
import no.nordicsemi.android.ui.view.SectionTitle
|
import no.nordicsemi.android.ui.view.SectionTitle
|
||||||
import no.nordicsemi.android.uart.R
|
import no.nordicsemi.android.uart.R
|
||||||
|
|
||||||
@@ -74,7 +73,7 @@ internal fun MacroSection(viewState: UARTViewState, onEvent: (UARTViewEvent) ->
|
|||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
) {
|
) {
|
||||||
ScreenSection {
|
OutlinedCard {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ internal fun UARTAddConfigurationDialog(onEvent: (UARTViewEvent) -> Unit, onDism
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun NameInput(
|
private fun NameInput(
|
||||||
name: MutableState<String>,
|
name: MutableState<String>,
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ import androidx.compose.ui.graphics.ColorFilter
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.theme.RadioButtonGroup
|
import no.nordicsemi.android.common.theme.view.RadioButtonGroup
|
||||||
import no.nordicsemi.android.theme.RadioButtonItem
|
import no.nordicsemi.android.common.theme.view.RadioButtonItem
|
||||||
import no.nordicsemi.android.theme.RadioGroupViewEntity
|
import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity
|
||||||
import no.nordicsemi.android.uart.R
|
import no.nordicsemi.android.uart.R
|
||||||
import no.nordicsemi.android.uart.data.MacroEol
|
import no.nordicsemi.android.uart.data.MacroEol
|
||||||
import no.nordicsemi.android.uart.data.MacroIcon
|
import no.nordicsemi.android.uart.data.MacroIcon
|
||||||
@@ -130,6 +130,7 @@ internal fun UARTAddMacroDialog(macro: UARTMacro?, onEvent: (UARTViewEvent) -> U
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun CommandInput(command: MutableState<String>) {
|
private fun CommandInput(command: MutableState<String>) {
|
||||||
Column {
|
Column {
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import no.nordicsemi.android.ui.view.dialog.*
|
import no.nordicsemi.android.ui.view.dialog.*
|
||||||
import no.nordicsemi.android.uart.R
|
import no.nordicsemi.android.uart.R
|
||||||
import no.nordicsemi.android.uart.data.UARTConfiguration
|
import no.nordicsemi.android.uart.data.UARTConfiguration
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun UARTConfigurationPicker(state: UARTViewState, onEvent: (UARTViewEvent) -> Unit) {
|
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]))
|
onEvent(OnConfigurationSelected(state.configurations[it.index]))
|
||||||
showDialog.value = false
|
showDialog.value = false
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,11 +32,11 @@
|
|||||||
package no.nordicsemi.android.uart.view
|
package no.nordicsemi.android.uart.view
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import no.nordicsemi.android.theme.Card
|
|
||||||
import no.nordicsemi.android.uart.data.UARTData
|
import no.nordicsemi.android.uart.data.UARTData
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -39,20 +39,27 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import no.nordicsemi.android.theme.PagerView
|
import no.nordicsemi.android.common.theme.view.PagerView
|
||||||
import no.nordicsemi.android.theme.PagerViewEntity
|
import no.nordicsemi.android.common.theme.view.PagerViewEntity
|
||||||
import no.nordicsemi.android.theme.PagerViewItem
|
import no.nordicsemi.android.common.theme.view.PagerViewItem
|
||||||
import no.nordicsemi.android.service.*
|
import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView
|
||||||
import no.nordicsemi.android.ui.view.BackIconAppBar
|
import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView
|
||||||
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
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.R
|
||||||
import no.nordicsemi.android.uart.data.UARTData
|
import no.nordicsemi.android.uart.data.UARTData
|
||||||
import no.nordicsemi.android.uart.viewmodel.UARTViewModel
|
import no.nordicsemi.android.uart.viewmodel.UARTViewModel
|
||||||
import no.nordicsemi.android.utils.exhaustive
|
import no.nordicsemi.android.ui.view.BackIconAppBar
|
||||||
import no.nordicsemi.ui.scanner.ui.DeviceConnectingView
|
import no.nordicsemi.android.ui.view.LoggerIconAppBar
|
||||||
import no.nordicsemi.ui.scanner.ui.DeviceDisconnectedView
|
import no.nordicsemi.android.ui.view.NavigateUpButton
|
||||||
import no.nordicsemi.ui.scanner.ui.NoDeviceView
|
|
||||||
import no.nordicsemi.ui.scanner.ui.Reason
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UARTScreen() {
|
fun UARTScreen() {
|
||||||
@@ -64,19 +71,21 @@ fun UARTScreen() {
|
|||||||
|
|
||||||
AppBar(state = state, navigateUp = navigateUp) { viewModel.onEvent(it) }
|
AppBar(state = state, navigateUp = navigateUp) { viewModel.onEvent(it) }
|
||||||
|
|
||||||
when (state.uartManagerState) {
|
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||||
NoDeviceState -> NoDeviceView()
|
when (state.uartManagerState) {
|
||||||
is WorkingState -> when (state.uartManagerState.result) {
|
NoDeviceState -> DeviceConnectingView()
|
||||||
is IdleResult,
|
is WorkingState -> when (state.uartManagerState.result) {
|
||||||
is ConnectingResult -> Scroll { DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } }
|
is IdleResult,
|
||||||
is ConnectedResult -> Scroll { DeviceConnectingView { viewModel.onEvent(DisconnectEvent) } }
|
is ConnectingResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is DisconnectedResult -> Scroll { DeviceDisconnectedView(Reason.USER, navigateUp) }
|
is ConnectedResult -> DeviceConnectingView { viewModel.onEvent(DisconnectEvent) }
|
||||||
is LinkLossResult -> Scroll { DeviceDisconnectedView(Reason.LINK_LOSS, navigateUp) }
|
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) }
|
||||||
is MissingServiceResult -> Scroll { DeviceDisconnectedView(Reason.MISSING_SERVICE, navigateUp) }
|
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
|
||||||
is UnknownErrorResult -> Scroll { DeviceDisconnectedView(Reason.UNKNOWN, navigateUp) }
|
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) { NavigateUpButton(navigateUp) }
|
||||||
is SuccessResult -> SuccessScreen(state.uartManagerState.result.data, state, viewModel)
|
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
||||||
|
is SuccessResult -> SuccessScreen(state.uartManagerState.result.data, state, viewModel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.exhaustive
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user