From 7a171a1402abf2abab28d4743c2846f291cda92c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Mon, 27 Sep 2021 13:50:48 +0200 Subject: [PATCH] Add HRS service --- app/build.gradle | 4 +- .../android/nrftoolbox/FeatureButton.kt | 54 +++++ .../android/nrftoolbox/HomeScreen.kt | 121 ++++++---- .../android/nrftoolbox/NavDestination.kt | 11 + .../android/nrftoolbox/NavigationViewModel.kt | 58 +++++ app/src/main/res/drawable/ic_hrs.xml | 4 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 1404 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 2898 -> 0 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 982 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 1772 -> 0 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 1900 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 3918 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 2884 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 5914 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 3844 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 7778 -> 0 bytes app/src/main/res/values/strings.xml | 3 +- feature_csc/build.gradle | 1 - feature_csc/src/main/AndroidManifest.xml | 3 - .../nordicsemi/android/csc/CscNavigation.kt | 19 -- .../android/csc/events/CSCServiceEvent.kt | 8 +- .../csc/service/CSCDataReadBroadcast.kt | 2 +- .../android/csc/service/CSCManager.kt | 16 +- .../android/csc/view/CSCViewConnectedState.kt | 53 ----- .../android/csc/view/CSCViewEvent.kt | 8 - .../android/csc/view/ConnectedView.kt | 74 ++++++ .../nordicsemi/android/csc/view/CscScreen.kt | 141 ++--------- .../android/csc/view/SelectWheelSizeDialog.kt | 63 ++++- .../android/csc/view/SensorsReadingView.kt | 49 ++-- .../android/csc/view/SpeedUnitRadioGroup.kt | 5 +- .../android/csc/view/WheelSizeView.kt | 5 +- .../csc/viewmodel/CSCViewConnectedState.kt | 58 +++++ .../android/csc/viewmodel/CscViewModel.kt | 51 +--- feature_csc/src/main/res/values/strings.xml | 5 - .../nordicsemi/android/csc/ExampleUnitTest.kt | 10 +- feature_hrs/build.gradle | 28 +++ .../android/hrs/ExampleInstrumentedTest.kt | 24 ++ feature_hrs/src/main/AndroidManifest.xml | 9 + .../android/hrs/events/HRSAggregatedData.kt | 7 + .../hrs/service/BodySensorLocationParser.kt | 40 ++++ .../android/hrs/service/HRSDataBroadcast.kt | 9 + .../android/hrs/service/HRSManager.kt | 162 +++++++++++++ .../hrs/service/HRSManagerCallbacks.kt | 29 +++ .../android/hrs/service/HRSService.kt | 53 +++++ .../hrs/service/HeartRateMeasurementParser.kt | 115 +++++++++ .../android/hrs/view/ContentView.kt | 224 ++++++++++++++++++ .../nordicsemi/android/hrs/view/HRSScreen.kt | 52 ++++ .../android/hrs/view/HRSScreenViewEvent.kt | 5 + .../android/hrs/viewmodel/HRSViewModel.kt | 47 ++++ .../android/hrs/viewmodel/HRSViewState.kt | 8 + .../src/main/res/drawable/fade_red.xml | 7 + feature_hrs/src/main/res/values/strings.xml | 4 + .../nordicsemi/android/hrs/ExampleUnitTest.kt | 17 ++ feature_scanner/build.gradle | 1 + .../nordicsemi/android/scanner/HiltModule.kt | 16 +- .../android/scanner/ScannerNavigation.kt | 41 ---- .../android/scanner/tools/NordicBleScanner.kt | 18 +- .../android/scanner/tools/PermissionHelper.kt | 21 ++ .../android/scanner/tools/ScannerStatus.kt | 4 +- .../view/BluetoothNotAvailableScreen.kt | 58 ++++- .../android/scanner/view/NotConnectedView.kt | 54 +++++ .../scanner/view/RequestPermissionScreen.kt | 58 +++-- .../android/scanner/view/ScanDeviceScreen.kt | 89 ++++--- .../viewmodel/BluetoothPermissionState.kt | 9 + .../viewmodel/NordicBleScannerViewModel.kt | 37 --- .../src/main/res/values/strings.xml | 15 +- lib_service/build.gradle | 2 +- lib_service/src/main/AndroidManifest.xml | 3 + .../android/service/BleProfileService.kt | 3 +- .../android/service/ForegroundBleService.kt | 55 +++-- .../service}/SelectedBluetoothDeviceHolder.kt | 2 +- lib_service/src/main/res/values/strings.xml | 2 + .../no/nordicsemi/android/theme/Background.kt | 24 -- .../java/no/nordicsemi/android/theme/Color.kt | 79 ++++-- .../java/no/nordicsemi/android/theme/Theme.kt | 59 +++-- .../android/theme/view/BatteryLevelView.kt | 28 +++ .../android/theme/view/KeyValueField.kt | 23 ++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 27 +++ .../mipmap-anydpi-v26/ic_launcher_round.xml | 27 +++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3129 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 4450 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5152 bytes .../main/res/mipmap-hdpi/ic_shortcut_dfu.png | Bin 0 -> 2723 bytes .../main/res/mipmap-hdpi/ic_shortcut_uart.png | Bin 0 -> 2196 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2048 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 2675 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 3182 bytes .../main/res/mipmap-mdpi/ic_shortcut_dfu.png | Bin 0 -> 1552 bytes .../main/res/mipmap-mdpi/ic_shortcut_uart.png | Bin 0 -> 1236 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4404 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 6341 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7362 bytes .../main/res/mipmap-xhdpi/ic_shortcut_dfu.png | Bin 0 -> 3264 bytes .../res/mipmap-xhdpi/ic_shortcut_uart.png | Bin 0 -> 2609 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6974 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 10833 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 12039 bytes .../res/mipmap-xxhdpi/ic_shortcut_dfu.png | Bin 0 -> 5903 bytes .../res/mipmap-xxhdpi/ic_shortcut_uart.png | Bin 0 -> 4876 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10114 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 16696 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 17708 bytes .../res/mipmap-xxxhdpi/ic_shortcut_dfu.png | Bin 0 -> 8109 bytes .../res/mipmap-xxxhdpi/ic_shortcut_uart.png | Bin 0 -> 6623 bytes .../src/main/res/values-night/colors.xml | 11 + lib_theme/src/main/res/values/colors.xml | 2 + lib_theme/src/main/res/values/strings.xml | 7 + lib_theme/src/main/res/values/themes.xml | 1 + .../java/no/nordicsemi/android/utils/Ext.kt | 17 ++ settings.gradle | 3 + 112 files changed, 1837 insertions(+), 635 deletions(-) create mode 100644 app/src/main/java/no/nordicsemi/android/nrftoolbox/FeatureButton.kt create mode 100644 app/src/main/java/no/nordicsemi/android/nrftoolbox/NavDestination.kt create mode 100644 app/src/main/java/no/nordicsemi/android/nrftoolbox/NavigationViewModel.kt create mode 100644 app/src/main/res/drawable/ic_hrs.xml delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp delete mode 100644 feature_csc/src/main/java/no/nordicsemi/android/csc/CscNavigation.kt delete mode 100644 feature_csc/src/main/java/no/nordicsemi/android/csc/view/CSCViewConnectedState.kt create mode 100644 feature_csc/src/main/java/no/nordicsemi/android/csc/view/ConnectedView.kt create mode 100644 feature_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewConnectedState.kt create mode 100644 feature_hrs/build.gradle create mode 100644 feature_hrs/src/androidTest/java/no/nordicsemi/android/hrs/ExampleInstrumentedTest.kt create mode 100644 feature_hrs/src/main/AndroidManifest.xml create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/events/HRSAggregatedData.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/BodySensorLocationParser.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSDataBroadcast.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSManager.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSManagerCallbacks.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HeartRateMeasurementParser.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/ContentView.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreenViewEvent.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt create mode 100644 feature_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewState.kt create mode 100644 feature_hrs/src/main/res/drawable/fade_red.xml create mode 100644 feature_hrs/src/main/res/values/strings.xml create mode 100644 feature_hrs/src/test/java/no/nordicsemi/android/hrs/ExampleUnitTest.kt delete mode 100644 feature_scanner/src/main/java/no/nordicsemi/android/scanner/ScannerNavigation.kt create mode 100644 feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/PermissionHelper.kt create mode 100644 feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/NotConnectedView.kt create mode 100644 feature_scanner/src/main/java/no/nordicsemi/android/scanner/viewmodel/BluetoothPermissionState.kt delete mode 100644 feature_scanner/src/main/java/no/nordicsemi/android/scanner/viewmodel/NordicBleScannerViewModel.kt rename {feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools => lib_service/src/main/java/no/nordicsemi/android/service}/SelectedBluetoothDeviceHolder.kt (94%) delete mode 100644 lib_theme/src/main/java/no/nordicsemi/android/theme/Background.kt create mode 100644 lib_theme/src/main/java/no/nordicsemi/android/theme/view/BatteryLevelView.kt create mode 100644 lib_theme/src/main/java/no/nordicsemi/android/theme/view/KeyValueField.kt create mode 100644 lib_theme/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 lib_theme/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 lib_theme/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 lib_theme/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 lib_theme/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 lib_theme/src/main/res/mipmap-hdpi/ic_shortcut_dfu.png create mode 100644 lib_theme/src/main/res/mipmap-hdpi/ic_shortcut_uart.png create mode 100644 lib_theme/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 lib_theme/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 lib_theme/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 lib_theme/src/main/res/mipmap-mdpi/ic_shortcut_dfu.png create mode 100644 lib_theme/src/main/res/mipmap-mdpi/ic_shortcut_uart.png create mode 100644 lib_theme/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 lib_theme/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 lib_theme/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 lib_theme/src/main/res/mipmap-xhdpi/ic_shortcut_dfu.png create mode 100644 lib_theme/src/main/res/mipmap-xhdpi/ic_shortcut_uart.png create mode 100644 lib_theme/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 lib_theme/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 lib_theme/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 lib_theme/src/main/res/mipmap-xxhdpi/ic_shortcut_dfu.png create mode 100644 lib_theme/src/main/res/mipmap-xxhdpi/ic_shortcut_uart.png create mode 100644 lib_theme/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 lib_theme/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 lib_theme/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 lib_theme/src/main/res/mipmap-xxxhdpi/ic_shortcut_dfu.png create mode 100644 lib_theme/src/main/res/mipmap-xxxhdpi/ic_shortcut_uart.png create mode 100644 lib_theme/src/main/res/values-night/colors.xml create mode 100644 lib_theme/src/main/res/values/strings.xml diff --git a/app/build.gradle b/app/build.gradle index ac2b5294..df211256 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,8 +51,10 @@ dependencies { //Hilt requires to implement every module in the main app module //https://github.com/google/dagger/issues/2123 implementation project(":feature_csc") - implementation project(":lib_theme") + implementation project(":feature_hrs") implementation project(':feature_scanner') + implementation project(":lib_theme") + implementation project(":lib_utils") implementation libs.nordic.ble.common diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/FeatureButton.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/FeatureButton.kt new file mode 100644 index 00000000..6aab9719 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/FeatureButton.kt @@ -0,0 +1,54 @@ +package no.nordicsemi.android.nrftoolbox + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.theme.NordicColors + +@Composable +fun FeatureButton(@DrawableRes iconId: Int, @StringRes nameId: Int, onClick: () -> Unit) { + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { onClick() }, + colors = ButtonDefaults.buttonColors(backgroundColor = NordicColors.NordicGray4.value()), + ) { + Image( + painter = painterResource(iconId), + contentDescription = stringResource(id = nameId), + contentScale = ContentScale.Crop, + modifier = Modifier + .size(64.dp) + .clip(CircleShape) + .background(Color.White) + ) + Row( + modifier = Modifier + .padding(16.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + Text( + text = stringResource(id = nameId), + modifier = Modifier.padding(16.dp), + ) + } + } +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeScreen.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeScreen.kt index b6d77b5a..9f32178b 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeScreen.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/HomeScreen.kt @@ -1,86 +1,105 @@ package no.nordicsemi.android.nrftoolbox -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement +import androidx.activity.OnBackPressedCallback +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Button -import androidx.compose.material.ButtonDefaults import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import no.nordicsemi.android.csc.CSCRoute +import no.nordicsemi.android.csc.view.CscScreen +import no.nordicsemi.android.hrs.view.HRSScreen +import no.nordicsemi.android.scanner.view.BluetoothNotAvailableScreen +import no.nordicsemi.android.scanner.view.BluetoothNotEnabledScreen +import no.nordicsemi.android.scanner.view.RequestPermissionScreen +import no.nordicsemi.android.scanner.view.ScanDeviceScreen +import no.nordicsemi.android.scanner.view.ScanDeviceScreenResult +import no.nordicsemi.android.utils.exhaustive @Composable fun HomeScreen() { val navController = rememberNavController() - NavHost(navController = navController, startDestination = "home") { - composable("home") { HomeView(navController) } - composable("csc-route") { CSCRoute() } + val viewModel = hiltViewModel() + val continueAction: () -> Unit = { viewModel.finish() } + val state = viewModel.state.collectAsState().value + + BackHandler { viewModel.navigateUp() } + + NavHost(navController = navController, startDestination = NavDestination.HOME.id) { + composable(NavDestination.HOME.id) { HomeView { viewModel.navigate(it) } } + composable(NavDestination.CSC.id) { CscScreen { viewModel.navigateUp() } } + composable(NavDestination.HRS.id) { HRSScreen { viewModel.navigateUp() } } + composable(NavDestination.REQUEST_PERMISSION.id) { RequestPermissionScreen(continueAction) } + composable(NavDestination.BLUETOOTH_NOT_AVAILABLE.id) { BluetoothNotAvailableScreen() } + composable(NavDestination.BLUETOOTH_NOT_ENABLED.id) { + BluetoothNotEnabledScreen(continueAction) + } + composable(NavDestination.DEVICE_NOT_CONNECTED.id) { + ScanDeviceScreen { + when (it) { + ScanDeviceScreenResult.SUCCESS -> viewModel.finish() + ScanDeviceScreenResult.CANCEL -> viewModel.navigateUp() + }.exhaustive + } + } + } + + LaunchedEffect(state) { + navController.navigate(state.id) } } @Composable -fun HomeView(navHostController: NavController) { +fun HomeView(callback: (NavDestination) -> Unit) { Column { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) - FeatureButton(R.drawable.ic_csc, R.string.csc_module) { navHostController.navigate("csc-route") } + FeatureButton(R.drawable.ic_csc, R.string.csc_module) { callback(NavDestination.CSC) } + FeatureButton(R.drawable.ic_hrs, R.string.hrs_module) { callback(NavDestination.HRS) } } } @Composable -fun FeatureButton(@DrawableRes iconId: Int, @StringRes nameId: Int, onClick: () -> Unit) { - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { onClick() }, - colors = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent) - ) { - Image( - painter = painterResource(iconId), - contentDescription = stringResource(id = nameId), - contentScale = ContentScale.Crop, - modifier = Modifier - .size(64.dp) - .clip(CircleShape) - .background(Color.White) - ) - Row( - modifier = Modifier - .padding(16.dp) - .fillMaxWidth(), - horizontalArrangement = Arrangement.Center - ) { - Text( - text = stringResource(id = nameId), - modifier = Modifier.padding(16.dp), - ) +private fun BackHandler(enabled: Boolean = true, onBack: () -> Unit) { + val currentOnBack = rememberUpdatedState(onBack) + val backCallback = remember { + object : OnBackPressedCallback(enabled) { + override fun handleOnBackPressed() { + currentOnBack.value() + } + } + } + SideEffect { + backCallback.isEnabled = enabled + } + val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) { + "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner" + }.onBackPressedDispatcher + val lifecycleOwner = LocalLifecycleOwner.current + DisposableEffect(lifecycleOwner, backDispatcher) { + backDispatcher.addCallback(lifecycleOwner, backCallback) + onDispose { + backCallback.remove() } } } + @Preview(showBackground = true) @Composable fun DefaultPreview() { - HomeView(rememberNavController()) + HomeView { } } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavDestination.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavDestination.kt new file mode 100644 index 00000000..19b87a19 --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavDestination.kt @@ -0,0 +1,11 @@ +package no.nordicsemi.android.nrftoolbox + +enum class NavDestination(val id: String) { + HOME("home-screen"), + CSC("csc-screen"), + HRS("hrs-screen"), + REQUEST_PERMISSION("request-permission"), + BLUETOOTH_NOT_AVAILABLE("bluetooth-not-available"), + BLUETOOTH_NOT_ENABLED("bluetooth-not-enabled"), + DEVICE_NOT_CONNECTED("device-not-connected"), +} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavigationViewModel.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavigationViewModel.kt new file mode 100644 index 00000000..cc9ce9ff --- /dev/null +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/NavigationViewModel.kt @@ -0,0 +1,58 @@ +package no.nordicsemi.android.nrftoolbox + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import no.nordicsemi.android.scanner.tools.NordicBleScanner +import no.nordicsemi.android.scanner.tools.PermissionHelper +import no.nordicsemi.android.scanner.tools.ScannerStatus +import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder +import no.nordicsemi.android.scanner.viewmodel.BluetoothPermissionState +import javax.inject.Inject + +@HiltViewModel +class NavigationViewModel @Inject constructor( + private val bleScanner: NordicBleScanner, + private val permissionHelper: PermissionHelper, + private val selectedDevice: no.nordicsemi.android.service.SelectedBluetoothDeviceHolder +): ViewModel() { + + val state= MutableStateFlow(NavDestination.HOME) + private var targetDestination = NavDestination.HOME + + fun navigate(destination: NavDestination) { + targetDestination = destination + navigateToNextScreen() + } + + fun navigateUp() { + targetDestination = NavDestination.HOME + state.value = NavDestination.HOME + } + + fun finish() { + if (state.value != targetDestination) { + navigateToNextScreen() + } + } + + private fun getBluetoothState(): BluetoothPermissionState { + return if (!permissionHelper.isRequiredPermissionGranted()) { + BluetoothPermissionState.PERMISSION_REQUIRED + } else when (bleScanner.getBluetoothStatus()) { + ScannerStatus.NOT_AVAILABLE -> BluetoothPermissionState.BLUETOOTH_NOT_AVAILABLE + ScannerStatus.DISABLED -> BluetoothPermissionState.BLUETOOTH_NOT_ENABLED + ScannerStatus.ENABLED -> selectedDevice.device?.let { BluetoothPermissionState.READY } ?: BluetoothPermissionState.DEVICE_NOT_CONNECTED + } + } + + private fun navigateToNextScreen() { + state.value = when (getBluetoothState()) { + BluetoothPermissionState.PERMISSION_REQUIRED -> NavDestination.REQUEST_PERMISSION + BluetoothPermissionState.BLUETOOTH_NOT_AVAILABLE -> NavDestination.BLUETOOTH_NOT_AVAILABLE + BluetoothPermissionState.BLUETOOTH_NOT_ENABLED -> NavDestination.BLUETOOTH_NOT_ENABLED + BluetoothPermissionState.DEVICE_NOT_CONNECTED -> NavDestination.DEVICE_NOT_CONNECTED + BluetoothPermissionState.READY -> targetDestination + } + } +} diff --git a/app/src/main/res/drawable/ic_hrs.xml b/app/src/main/res/drawable/ic_hrs.xml new file mode 100644 index 00000000..e4324138 --- /dev/null +++ b/app/src/main/res/drawable/ic_hrs.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cfe..00000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cfe..00000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78ecd372343283f4157dcfd918ec5165bb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2898 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 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a3070fe34c611c42c0d3ad3013a0dce358be0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fadc34db..7abbce26 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ - nRF Toolbox - CSC + HRS \ No newline at end of file diff --git a/feature_csc/build.gradle b/feature_csc/build.gradle index 0b49fa39..d397c91b 100644 --- a/feature_csc/build.gradle +++ b/feature_csc/build.gradle @@ -4,7 +4,6 @@ apply plugin: 'kotlin-parcelize' dependencies { implementation project(":lib_service") implementation project(":lib_theme") - implementation project(':feature_scanner') implementation project(":lib_utils") implementation libs.nordic.ble.common diff --git a/feature_csc/src/main/AndroidManifest.xml b/feature_csc/src/main/AndroidManifest.xml index aaee1182..bca3c340 100644 --- a/feature_csc/src/main/AndroidManifest.xml +++ b/feature_csc/src/main/AndroidManifest.xml @@ -1,12 +1,9 @@ - diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/CscNavigation.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/CscNavigation.kt deleted file mode 100644 index be8462b2..00000000 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/CscNavigation.kt +++ /dev/null @@ -1,19 +0,0 @@ -package no.nordicsemi.android.csc - -import androidx.compose.runtime.Composable -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController -import no.nordicsemi.android.csc.view.CscScreen -import no.nordicsemi.android.scanner.ScannerRoute - -@Composable -fun CSCRoute() { - - val navController = rememberNavController() - - NavHost(navController = navController, startDestination = "csc_screen") { - composable("csc_screen") { CscScreen(navController) } - composable("scanner-destination") { ScannerRoute(navController) } - } -} diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/events/CSCServiceEvent.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/events/CSCServiceEvent.kt index 64f7de80..41674a26 100644 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/events/CSCServiceEvent.kt +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/events/CSCServiceEvent.kt @@ -4,10 +4,10 @@ import android.bluetooth.BluetoothDevice import android.os.Parcelable import kotlinx.parcelize.Parcelize -sealed class CSCServiceEvent : Parcelable +internal sealed class CSCServiceEvent : Parcelable @Parcelize -data class OnDistanceChangedEvent( +internal data class OnDistanceChangedEvent( val bluetoothDevice: BluetoothDevice, val speed: Float, val distance: Float, @@ -15,14 +15,14 @@ data class OnDistanceChangedEvent( ) : CSCServiceEvent() @Parcelize -data class CrankDataChanged( +internal data class CrankDataChanged( val bluetoothDevice: BluetoothDevice, val crankCadence: Int, val gearRatio: Float ) : CSCServiceEvent() @Parcelize -data class OnBatteryLevelChanged( +internal data class OnBatteryLevelChanged( val device: BluetoothDevice, val batteryLevel: Int ) : CSCServiceEvent() diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/service/CSCDataReadBroadcast.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/service/CSCDataReadBroadcast.kt index 930d1d8c..9a854e5e 100644 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/service/CSCDataReadBroadcast.kt +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/service/CSCDataReadBroadcast.kt @@ -9,7 +9,7 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class CSCDataReadBroadcast @Inject constructor() : BluetoothDataReadBroadcast() { +internal class CSCDataReadBroadcast @Inject constructor() : BluetoothDataReadBroadcast() { private val _wheelSize = MutableSharedFlow( replay = 1, diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/service/CSCManager.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/service/CSCManager.kt index 22a11caa..2e90c1cf 100644 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/service/CSCManager.kt +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/service/CSCManager.kt @@ -35,6 +35,12 @@ import no.nordicsemi.android.log.LogContract import no.nordicsemi.android.service.BatteryManager import java.util.* +/** Cycling Speed and Cadence service UUID. */ +private val CYCLING_SPEED_AND_CADENCE_SERVICE_UUID = UUID.fromString("00001816-0000-1000-8000-00805f9b34fb") + +/** Cycling Speed and Cadence Measurement characteristic UUID. */ +private val CSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A5B-0000-1000-8000-00805f9b34fb") + internal class CSCManager(context: Context) : BatteryManager(context) { private var cscMeasurementCharacteristic: BluetoothGattCharacteristic? = null @@ -114,14 +120,4 @@ internal class CSCManager(context: Context) : BatteryManager Unit) { + if (state.showDialog) { + SelectWheelSizeDialog { onEvent(it) } + } + + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + SettingsSection(state, onEvent) + + Spacer(modifier = Modifier.height(16.dp)) + + SensorsReadingView(state = state) + + Spacer(modifier = Modifier.height(16.dp)) + + Button( + colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.secondary), + onClick = { onEvent(OnDisconnectButtonClick) } + ) { + Text(text = stringResource(id = R.string.disconnect)) + } + } +} + +@Composable +private fun SettingsSection(state: CSCViewState, onEvent: (CSCViewEvent) -> Unit) { + Card( + backgroundColor = NordicColors.NordicGray4.value(), + shape = RoundedCornerShape(10.dp), + elevation = 0.dp + ) { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + WheelSizeView(state, onEvent) + + Spacer(modifier = Modifier.height(16.dp)) + + SpeedUnitRadioGroup(state.selectedSpeedUnit) { onEvent(it) } + } + } +} + +@Preview +@Composable +private fun ConnectedPreview() { + ContentView(CSCViewState()) { } +} diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/CscScreen.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/CscScreen.kt index 3e5311b7..12dae03e 100644 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/CscScreen.kt +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/CscScreen.kt @@ -1,143 +1,52 @@ package no.nordicsemi.android.csc.view -import android.bluetooth.BluetoothDevice import android.content.Intent -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavController import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.service.CSCService +import no.nordicsemi.android.csc.viewmodel.CSCViewState import no.nordicsemi.android.csc.viewmodel.CscViewModel -import no.nordicsemi.android.utils.exhaustive import no.nordicsemi.android.utils.isServiceRunning @Composable -internal fun CscScreen(navController: NavController, viewModel: CscViewModel = hiltViewModel()) { - - val secondScreenResult = navController.currentBackStackEntry - ?.savedStateHandle - ?.getLiveData("result")?.observeAsState() - - secondScreenResult?.value?.let { - viewModel.onEvent(OnBluetoothDeviceSelected(it)) - - navController.currentBackStackEntry - ?.savedStateHandle - ?.set("result", null) - } - +fun CscScreen(finishAction: () -> Unit) { + val viewModel: CscViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - CSCView(navController, state) { viewModel.onEvent(it) } + val context = LocalContext.current + LaunchedEffect(state.isScreenActive) { + if (!state.isScreenActive) { + finishAction() + } + if (context.isServiceRunning(CSCService::class.java.name)) { + val intent = Intent(context, CSCService::class.java) + context.stopService(intent) + } + } + + LaunchedEffect("start-service") { + if (!context.isServiceRunning(CSCService::class.java.name)) { + val intent = Intent(context, CSCService::class.java) + context.startService(intent) + } + } + + CSCView(state) { viewModel.onEvent(it) } } @Composable -private fun CSCView(navController: NavController, state: CSCViewState, onEvent: (CSCViewEvent) -> Unit) { +private fun CSCView(state: CSCViewState, onEvent: (CSCViewEvent) -> Unit) { Column { TopAppBar(title = { Text(text = stringResource(id = R.string.csc_title)) }) - when (state) { - is CSCViewConnectedState -> ConnectedView(state) { onEvent(it) } - is CSCViewNotConnectedState -> NotConnectedScreen(navController, state) { - onEvent(it) - } - }.exhaustive + ContentView(state) { onEvent(it) } } } - -@Composable -private fun NotConnectedScreen( - navController: NavController, - state: CSCViewNotConnectedState, - onEvent: (CSCViewEvent) -> Unit -) { - if (state.showScannerDialog) { - navController.navigate("scanner-destination") - onEvent(OnMovedToScannerScreen) - } - - if (LocalContext.current.isServiceRunning(CSCService::class.java.name)) { - val intent = Intent(LocalContext.current, CSCService::class.java) - LocalContext.current.stopService(intent) - } - - NotConnectedView(onEvent) - - LocalContext.current.stopService(Intent(LocalContext.current, CSCService::class.java)) -} - -@Composable -private fun NotConnectedView( - onEvent: (CSCViewEvent) -> Unit -) { - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(text = stringResource(id = R.string.csc_no_connection)) - Spacer(modifier = Modifier.height(16.dp)) - Button(onClick = { onEvent(OnConnectButtonClick) }) { - Text(text = stringResource(id = R.string.csc_connect)) - } - } -} - -@Composable -private fun ConnectedView(state: CSCViewConnectedState, onEvent: (CSCViewEvent) -> Unit) { - if (state.showDialog) { - SelectWheelSizeDialog { onEvent(it) } - } - - if (!LocalContext.current.isServiceRunning(CSCService::class.java.name)) { - val intent = Intent(LocalContext.current, CSCService::class.java) - LocalContext.current.startService(intent) - } - - Column( - modifier = Modifier.padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - WheelSizeView(state, onEvent) - - SpeedUnitRadioGroup(state.selectedSpeedUnit) { onEvent(it) } - - SensorsReadingView(state = state) - - Button(onClick = { onEvent(OnDisconnectButtonClick) }) { - Text(text = stringResource(id = R.string.csc_disconnect)) - } - } -} - -@Preview -@Composable -private fun NotConnectedPreview() { - NotConnectedView { } -} - -@Preview -@Composable -private fun ConnectedPreview() { - ConnectedView(CSCViewConnectedState()) { } -} diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SelectWheelSizeDialog.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SelectWheelSizeDialog.kt index 5280fc6d..a4cc852b 100644 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SelectWheelSizeDialog.kt +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SelectWheelSizeDialog.kt @@ -1,18 +1,29 @@ package no.nordicsemi.android.csc.view import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Card +import androidx.compose.material.TabRowDefaults.Divider import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringArrayResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import no.nordicsemi.android.csc.R -import no.nordicsemi.android.theme.Background +import no.nordicsemi.android.theme.NordicColors +import no.nordicsemi.android.theme.NordicColors.NordicLightGray import no.nordicsemi.android.theme.TestTheme @Composable @@ -27,13 +38,47 @@ private fun SelectWheelSizeView(onEvent: (OnWheelSizeSelected) -> Unit) { val wheelEntries = stringArrayResource(R.array.wheel_entries) val wheelValues = stringArrayResource(R.array.wheel_values) - Box(Modifier.padding(16.dp)) { - Column(modifier = Background.whiteRoundedCorners()) { - Text(text = "Wheel size") - wheelEntries.forEachIndexed { i, entry -> - Text(text = entry, modifier = Modifier.clickable { - onEvent(OnWheelSizeSelected(wheelValues[i].toInt(), entry)) - }) + Card( + modifier = Modifier.height(300.dp), + backgroundColor = NordicColors.NordicGray4.value(), + shape = RoundedCornerShape(10.dp), + elevation = 0.dp + ) { + Column { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Wheel size", + fontSize = 28.sp, + fontWeight = FontWeight.Bold + ) + } + + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .padding(16.dp) + ) { + + wheelEntries.forEachIndexed { i, entry -> + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = entry, + fontSize = 16.sp, + modifier = Modifier + .fillMaxWidth() + .clickable { + onEvent(OnWheelSizeSelected(wheelValues[i].toInt(), entry)) + } + ) + + if (i != wheelEntries.size - 1) { + Spacer(modifier = Modifier.height(4.dp)) + Divider(color = NordicLightGray.value(), thickness = 1.dp/2) + } + } } } } diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt index 893cd4af..59e5c568 100644 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SensorsReadingView.kt @@ -1,55 +1,52 @@ package no.nordicsemi.android.csc.view -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.material.Text +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.csc.R -import no.nordicsemi.android.theme.Background +import no.nordicsemi.android.csc.viewmodel.CSCViewState +import no.nordicsemi.android.theme.NordicColors +import no.nordicsemi.android.theme.view.BatteryLevelView +import no.nordicsemi.android.theme.view.KeyValueField @Composable -internal fun SensorsReadingView(state: CSCViewConnectedState) { - Column { - Column(modifier = Background.whiteRoundedCorners()) { +internal fun SensorsReadingView(state: CSCViewState) { + Card( + backgroundColor = NordicColors.NordicGray4.value(), + shape = RoundedCornerShape(10.dp), + elevation = 0.dp + ) { + Column(modifier = Modifier.padding(16.dp)) { KeyValueField(stringResource(id = R.string.scs_field_speed), state.displaySpeed()) + Spacer(modifier = Modifier.height(4.dp)) KeyValueField(stringResource(id = R.string.scs_field_cadence), state.displayCadence()) + Spacer(modifier = Modifier.height(4.dp)) KeyValueField(stringResource(id = R.string.scs_field_distance), state.displayDistance()) + Spacer(modifier = Modifier.height(4.dp)) KeyValueField( stringResource(id = R.string.scs_field_total_distance), state.displayTotalDistance() ) - KeyValueField(stringResource(id = R.string.scs_field_gear_ratio), state.displaySpeed()) - } - - Spacer(modifier = Modifier.height(16.dp)) - - Column(modifier = Background.whiteRoundedCorners()) { - KeyValueField(stringResource(id = R.string.scs_field_battery), state.displayBatteryLever()) + Spacer(modifier = Modifier.height(4.dp)) + KeyValueField(stringResource(id = R.string.scs_field_gear_ratio), state.displayGearRatio()) } } -} -@Composable -private fun KeyValueField(key: String, value: String) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text(text = key) - Text(text = value) - } + Spacer(modifier = Modifier.height(16.dp)) + + BatteryLevelView(state.batteryLevel) } @Preview @Composable private fun Preview() { - SensorsReadingView(CSCViewConnectedState()) + SensorsReadingView(CSCViewState()) } diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SpeedUnitRadioGroup.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SpeedUnitRadioGroup.kt index bb10069c..36b51e83 100644 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SpeedUnitRadioGroup.kt +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/SpeedUnitRadioGroup.kt @@ -2,8 +2,10 @@ package no.nordicsemi.android.csc.view import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.RadioButton import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -16,7 +18,7 @@ internal fun SpeedUnitRadioGroup( onEvent: (OnSelectedSpeedUnitSelected) -> Unit ) { Row( - modifier = Modifier.fillMaxWidth().padding(16.dp), + modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly ) { SpeedUnitRadioButton(currentUnit, SpeedUnit.KM_H, onEvent) @@ -36,6 +38,7 @@ internal fun SpeedUnitRadioButton( selected = (selectedUnit == displayedUnit), onClick = { onEvent(OnSelectedSpeedUnitSelected(displayedUnit)) } ) + Spacer(modifier = Modifier.width(4.dp)) Text(text = createSpeedUnitLabel(displayedUnit)) } } diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt index 909018ef..76f69dd1 100644 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/view/WheelSizeView.kt @@ -12,9 +12,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import no.nordicsemi.android.csc.R +import no.nordicsemi.android.csc.viewmodel.CSCViewState @Composable -internal fun WheelSizeView(state: CSCViewConnectedState, onEvent: (CSCViewEvent) -> Unit) { +internal fun WheelSizeView(state: CSCViewState, onEvent: (CSCViewEvent) -> Unit) { OutlinedTextField( modifier = Modifier.fillMaxWidth(), value = state.wheelSize, @@ -35,5 +36,5 @@ private fun EditIcon(onEvent: (CSCViewEvent) -> Unit) { @Preview @Composable private fun WheelSizeViewPreview() { - WheelSizeView(CSCViewConnectedState()) { } + WheelSizeView(CSCViewState()) { } } diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewConnectedState.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewConnectedState.kt new file mode 100644 index 00000000..b63c5970 --- /dev/null +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewConnectedState.kt @@ -0,0 +1,58 @@ +package no.nordicsemi.android.csc.viewmodel + +import no.nordicsemi.android.csc.view.CSCSettings +import no.nordicsemi.android.csc.view.SpeedUnit +import java.util.* + +internal data class CSCViewState( + val showDialog: Boolean = false, + val scanDevices: Boolean = false, + val selectedSpeedUnit: SpeedUnit = SpeedUnit.M_S, + val speed: Float = 0f, + val cadence: Int = 0, + val distance: Float = 0f, + val totalDistance: Float = 0f, + val gearRatio: Float = 0f, + val batteryLevel: Int = 0, + val wheelSize: String = CSCSettings.DefaultWheelSize.NAME, + val isScreenActive: Boolean = true +) { + + private val speedWithUnit = when (selectedSpeedUnit) { + SpeedUnit.M_S -> speed + SpeedUnit.KM_H -> speed * 3.6f + SpeedUnit.MPH -> speed * 2.2369f + } + + fun displaySpeed(): String { + return when (selectedSpeedUnit) { + SpeedUnit.M_S -> String.format("%.1f m/s", speedWithUnit) + SpeedUnit.KM_H -> String.format("%.1f km/h", speedWithUnit) + SpeedUnit.MPH -> String.format("%.1f mph", speedWithUnit) + } + } + + fun displayCadence(): String { + return String.format("%d RPM", cadence) + } + + fun displayDistance(): String { + return when (selectedSpeedUnit) { + SpeedUnit.M_S -> String.format("%.0f m", distance) + SpeedUnit.KM_H -> String.format("%.0f m", distance) + SpeedUnit.MPH -> String.format("%.0f yd", distance) + } + } + + fun displayTotalDistance(): String { + return when (selectedSpeedUnit) { + SpeedUnit.M_S -> String.format("%.2f km", distance) + SpeedUnit.KM_H -> String.format("%.2f km", distance) + SpeedUnit.MPH -> String.format("%.2f mile", distance) + } + } + + fun displayGearRatio(): String { + return String.format(Locale.US, "%.1f", gearRatio) + } +} diff --git a/feature_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CscViewModel.kt b/feature_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CscViewModel.kt index b2cde153..1f5d2910 100644 --- a/feature_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CscViewModel.kt +++ b/feature_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CscViewModel.kt @@ -13,28 +13,20 @@ import no.nordicsemi.android.csc.events.CrankDataChanged import no.nordicsemi.android.csc.events.OnBatteryLevelChanged import no.nordicsemi.android.csc.events.OnDistanceChangedEvent import no.nordicsemi.android.csc.service.CSCDataReadBroadcast -import no.nordicsemi.android.csc.view.CSCViewConnectedState import no.nordicsemi.android.csc.view.CSCViewEvent -import no.nordicsemi.android.csc.view.CSCViewNotConnectedState -import no.nordicsemi.android.csc.view.CSCViewState -import no.nordicsemi.android.csc.view.OnBluetoothDeviceSelected -import no.nordicsemi.android.csc.view.OnConnectButtonClick import no.nordicsemi.android.csc.view.OnDisconnectButtonClick -import no.nordicsemi.android.csc.view.OnMovedToScannerScreen import no.nordicsemi.android.csc.view.OnSelectedSpeedUnitSelected import no.nordicsemi.android.csc.view.OnShowEditWheelSizeDialogButtonClick import no.nordicsemi.android.csc.view.OnWheelSizeSelected -import no.nordicsemi.android.scanner.tools.SelectedBluetoothDeviceHolder import no.nordicsemi.android.utils.exhaustive import javax.inject.Inject @HiltViewModel internal class CscViewModel @Inject constructor( - private val localBroadcast: CSCDataReadBroadcast, - private val deviceHolder: SelectedBluetoothDeviceHolder + private val localBroadcast: CSCDataReadBroadcast ) : ViewModel() { - val state = MutableStateFlow(createInitialState()) + val state = MutableStateFlow(CSCViewState()) init { localBroadcast.events.onEach { @@ -42,10 +34,6 @@ internal class CscViewModel @Inject constructor( }.launchIn(viewModelScope) } - private fun createInitialState(): CSCViewState { - return deviceHolder.device?.let { CSCViewConnectedState() } ?: CSCViewNotConnectedState() - } - private fun consumeEvent(event: CSCServiceEvent) { val newValue = when (event) { is CrankDataChanged -> createNewState(event) @@ -55,21 +43,21 @@ internal class CscViewModel @Inject constructor( state.value = newValue } - private fun createNewState(event: CrankDataChanged): CSCViewConnectedState { - return state.value.ensureConnectedState().copy( + private fun createNewState(event: CrankDataChanged): CSCViewState { + return state.value.copy( cadence = event.crankCadence, gearRatio = event.gearRatio ) } - private fun createNewState(event: OnBatteryLevelChanged): CSCViewConnectedState { - return state.value.ensureConnectedState().copy( + private fun createNewState(event: OnBatteryLevelChanged): CSCViewState { + return state.value.copy( batteryLevel = event.batteryLevel ) } - private fun createNewState(event: OnDistanceChangedEvent): CSCViewConnectedState { - return state.value.ensureConnectedState().copy( + private fun createNewState(event: OnDistanceChangedEvent): CSCViewState { + return state.value.copy( speed = event.speed, distance = event.distance, totalDistance = event.totalDistance @@ -82,41 +70,26 @@ internal class CscViewModel @Inject constructor( OnShowEditWheelSizeDialogButtonClick -> onShowDialogEvent() is OnWheelSizeSelected -> onWheelSizeChanged(event) OnDisconnectButtonClick -> onDisconnectButtonClick() - OnConnectButtonClick -> onConnectButtonClick() - OnMovedToScannerScreen -> onOnMovedToScannerScreen() - is OnBluetoothDeviceSelected -> onBluetoothDeviceSelected() }.exhaustive } private fun onSelectedSpeedUnit(event: OnSelectedSpeedUnitSelected) { - state.tryEmit(state.value.ensureConnectedState().copy(selectedSpeedUnit = event.selectedSpeedUnit)) + state.tryEmit(state.value.copy(selectedSpeedUnit = event.selectedSpeedUnit)) } private fun onShowDialogEvent() { - state.tryEmit(state.value.ensureConnectedState().copy(showDialog = true)) + state.tryEmit(state.value.copy(showDialog = true)) } private fun onWheelSizeChanged(event: OnWheelSizeSelected) { localBroadcast.setWheelSize(event.wheelSize) - state.tryEmit(state.value.ensureConnectedState().copy( + state.tryEmit(state.value.copy( showDialog = false, wheelSize = event.wheelSizeDisplayInfo )) } private fun onDisconnectButtonClick() { - state.tryEmit(CSCViewNotConnectedState()) - } - - private fun onConnectButtonClick() { - state.tryEmit(state.value.ensureDisconnectedState().copy(showScannerDialog = true)) - } - - private fun onOnMovedToScannerScreen() { - state.tryEmit(state.value.ensureDisconnectedState().copy(showScannerDialog = false)) - } - - private fun onBluetoothDeviceSelected() { - state.tryEmit(CSCViewConnectedState()) + state.tryEmit(state.value.copy(isScreenActive = false)) } } diff --git a/feature_csc/src/main/res/values/strings.xml b/feature_csc/src/main/res/values/strings.xml index 9a0b11a9..c38b30c3 100644 --- a/feature_csc/src/main/res/values/strings.xml +++ b/feature_csc/src/main/res/values/strings.xml @@ -2,16 +2,11 @@ Cyclic and speed cadence - Disconnect - No device connected - Connect - Speed Cadence Distance Total Distance Gear Ratio - Battery Wheel size diff --git a/feature_csc/src/test/java/no/nordicsemi/android/csc/ExampleUnitTest.kt b/feature_csc/src/test/java/no/nordicsemi/android/csc/ExampleUnitTest.kt index ef4fed15..f140885a 100644 --- a/feature_csc/src/test/java/no/nordicsemi/android/csc/ExampleUnitTest.kt +++ b/feature_csc/src/test/java/no/nordicsemi/android/csc/ExampleUnitTest.kt @@ -1,9 +1,9 @@ package no.nordicsemi.android.csc +import androidx.annotation.FloatRange +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). * @@ -12,6 +12,12 @@ import org.junit.Assert.* class ExampleUnitTest { @Test fun addition_isCorrect() { + + println("red: ${colorToHex(0f)}") + println("green: ${colorToHex(169f)}") + println("blue: ${colorToHex(206f)}") assertEquals(4, 2 + 2) } + + private fun colorToHex(@FloatRange(from = 0.0, to = 1.0) value: Float) = Integer.toHexString((0xFF * value).toInt()) } \ No newline at end of file diff --git a/feature_hrs/build.gradle b/feature_hrs/build.gradle new file mode 100644 index 00000000..662e3f13 --- /dev/null +++ b/feature_hrs/build.gradle @@ -0,0 +1,28 @@ +apply from: rootProject.file("library.gradle") +apply plugin: 'kotlin-parcelize' + +dependencies { + implementation project(":lib_service") + implementation project(":lib_theme") + implementation project(":lib_utils") + + implementation libs.chart + + implementation libs.nordic.ble.common + + implementation libs.nordic.log + + implementation libs.bundles.compose + implementation libs.androidx.core + implementation libs.material + implementation libs.lifecycle.activity + implementation libs.lifecycle.service + implementation libs.compose.lifecycle + implementation libs.compose.activity + + testImplementation libs.test.junit + androidTestImplementation libs.android.test.junit + androidTestImplementation libs.android.test.espresso + androidTestImplementation libs.android.test.compose.ui + debugImplementation libs.android.test.compose.tooling +} diff --git a/feature_hrs/src/androidTest/java/no/nordicsemi/android/hrs/ExampleInstrumentedTest.kt b/feature_hrs/src/androidTest/java/no/nordicsemi/android/hrs/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..8e759ebc --- /dev/null +++ b/feature_hrs/src/androidTest/java/no/nordicsemi/android/hrs/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package no.nordicsemi.android.hrs + +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.hrs.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature_hrs/src/main/AndroidManifest.xml b/feature_hrs/src/main/AndroidManifest.xml new file mode 100644 index 00000000..f623e468 --- /dev/null +++ b/feature_hrs/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/events/HRSAggregatedData.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/events/HRSAggregatedData.kt new file mode 100644 index 00000000..700be485 --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/events/HRSAggregatedData.kt @@ -0,0 +1,7 @@ +package no.nordicsemi.android.hrs.events + +internal data class HRSAggregatedData( + val heartRates: List = emptyList(), + val batteryLevel: Int = 0, + val sensorLocation: Int = 0 +) diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/BodySensorLocationParser.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/BodySensorLocationParser.kt new file mode 100644 index 00000000..6b1d706a --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/BodySensorLocationParser.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.hrs.service + +import no.nordicsemi.android.ble.data.Data + +object BodySensorLocationParser { + fun parse(data: Data): String { + val value = data.getIntValue(Data.FORMAT_UINT8, 0)!! + return when (value) { + 6 -> "Foot" + 5 -> "Ear Lobe" + 4 -> "Hand" + 3 -> "Finger" + 2 -> "Wrist" + 1 -> "Chest" + 0 -> "Other" + else -> "Other" + } + } +} \ No newline at end of file diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSDataBroadcast.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSDataBroadcast.kt new file mode 100644 index 00000000..29eddcf5 --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSDataBroadcast.kt @@ -0,0 +1,9 @@ +package no.nordicsemi.android.hrs.service + +import no.nordicsemi.android.hrs.events.HRSAggregatedData +import no.nordicsemi.android.service.BluetoothDataReadBroadcast +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class HRSDataBroadcast @Inject constructor() : BluetoothDataReadBroadcast() diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSManager.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSManager.kt new file mode 100644 index 00000000..76e74287 --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSManager.kt @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.hrs.service + +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCharacteristic +import android.content.Context +import android.util.Log +import androidx.annotation.IntRange +import no.nordicsemi.android.ble.common.callback.hr.BodySensorLocationDataCallback +import no.nordicsemi.android.ble.common.callback.hr.HeartRateMeasurementDataCallback +import no.nordicsemi.android.ble.common.profile.hr.BodySensorLocation +import no.nordicsemi.android.ble.data.Data +import no.nordicsemi.android.log.LogContract +import no.nordicsemi.android.service.BatteryManager +import java.util.* + +/** + * HRSManager class performs BluetoothGatt operations for connection, service discovery, + * enabling notification and reading characteristics. + * All operations required to connect to device with BLE Heart Rate Service and reading + * heart rate values are performed here. + */ +class HRSManager(context: Context) : BatteryManager(context) { + + private var heartRateCharacteristic: BluetoothGattCharacteristic? = null + private var bodySensorLocationCharacteristic: BluetoothGattCharacteristic? = null + + override fun getGattCallback(): BatteryManagerGattCallback { + return HeartRateManagerCallback() + } + + /** + * BluetoothGatt callbacks for connection/disconnection, service discovery, + * receiving notification, etc. + */ + private inner class HeartRateManagerCallback : BatteryManagerGattCallback() { + override fun initialize() { + super.initialize() + readCharacteristic(bodySensorLocationCharacteristic) + .with(object : BodySensorLocationDataCallback() { + + override fun onDataReceived(device: BluetoothDevice, data: Data) { + log( + LogContract.Log.Level.APPLICATION, + "\"" + BodySensorLocationParser.parse(data) + "\" received" + ) + super.onDataReceived(device, data) + } + + override fun onBodySensorLocationReceived( + device: BluetoothDevice, + @BodySensorLocation sensorLocation: Int + ) { + mCallbacks?.onBodySensorLocationReceived(device, sensorLocation) + } + + }) + .fail { device: BluetoothDevice?, status: Int -> + log(Log.WARN, "Body Sensor Location characteristic not found") + } + .enqueue() + + setNotificationCallback(heartRateCharacteristic) + .with(object : HeartRateMeasurementDataCallback() { + + override fun onDataReceived(device: BluetoothDevice, data: Data) { + log( + LogContract.Log.Level.APPLICATION, + "\"" + HeartRateMeasurementParser.parse(data) + "\" received" + ) + super.onDataReceived(device, data) + } + + override fun onHeartRateMeasurementReceived( + device: BluetoothDevice, + @IntRange(from = 0) heartRate: Int, + contactDetected: Boolean?, + @IntRange(from = 0) energyExpanded: Int?, + rrIntervals: List? + ) { + mCallbacks?.onHeartRateMeasurementReceived( + device, + heartRate, + contactDetected, + energyExpanded, + rrIntervals + ) + } + }) + enableNotifications(heartRateCharacteristic).enqueue() + } + + override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { + val service = gatt.getService(HR_SERVICE_UUID) + if (service != null) { + heartRateCharacteristic = service.getCharacteristic( + HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID + ) + } + return heartRateCharacteristic != null + } + + override fun isOptionalServiceSupported(gatt: BluetoothGatt): Boolean { + super.isOptionalServiceSupported(gatt) + val service = gatt.getService(HR_SERVICE_UUID) + if (service != null) { + bodySensorLocationCharacteristic = service.getCharacteristic( + BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID + ) + } + return bodySensorLocationCharacteristic != null + } + + override fun onDeviceDisconnected() { + super.onDeviceDisconnected() + bodySensorLocationCharacteristic = null + heartRateCharacteristic = null + } + + override fun onServicesInvalidated() {} + } + + companion object { + + val HR_SERVICE_UUID = UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb") + private val BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID = UUID.fromString("00002A38-0000-1000-8000-00805f9b34fb") + private val HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A37-0000-1000-8000-00805f9b34fb") + private var managerInstance: HRSManager? = null + + /** + * Singleton implementation of HRSManager class. + */ + @Synchronized + fun getInstance(context: Context): HRSManager? { + if (managerInstance == null) { + managerInstance = HRSManager(context) + } + return managerInstance + } + } +} \ No newline at end of file diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSManagerCallbacks.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSManagerCallbacks.kt new file mode 100644 index 00000000..dbb1cb6a --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSManagerCallbacks.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.hrs.service + +import no.nordicsemi.android.ble.common.profile.hr.BodySensorLocationCallback +import no.nordicsemi.android.ble.common.profile.hr.HeartRateMeasurementCallback +import no.nordicsemi.android.service.BatteryManagerCallbacks + +interface HRSManagerCallbacks + : BatteryManagerCallbacks, BodySensorLocationCallback, HeartRateMeasurementCallback \ No newline at end of file diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt new file mode 100644 index 00000000..a8d94192 --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -0,0 +1,53 @@ +package no.nordicsemi.android.hrs.service + +import android.bluetooth.BluetoothDevice +import dagger.hilt.android.AndroidEntryPoint +import no.nordicsemi.android.ble.BleManagerCallbacks +import no.nordicsemi.android.hrs.events.HRSAggregatedData +import no.nordicsemi.android.service.ForegroundBleService +import no.nordicsemi.android.service.LoggableBleManager +import javax.inject.Inject + +@AndroidEntryPoint +internal class HRSService : ForegroundBleService(), HRSManagerCallbacks { + + private var data = HRSAggregatedData() + private val points = mutableListOf() + + @Inject + lateinit var localBroadcast: HRSDataBroadcast + + override val manager: HRSManager by lazy { + HRSManager(this).apply { + setGattCallbacks(this@HRSService) + } + } + + override fun initializeManager(): LoggableBleManager { + return manager + } + + override fun onBatteryLevelChanged(device: BluetoothDevice, batteryLevel: Int) { + sendNewData(data.copy(batteryLevel = batteryLevel)) + } + + override fun onBodySensorLocationReceived(device: BluetoothDevice, sensorLocation: Int) { + sendNewData(data.copy(sensorLocation = sensorLocation)) + } + + override fun onHeartRateMeasurementReceived( + device: BluetoothDevice, + heartRate: Int, + contactDetected: Boolean?, + energyExpanded: Int?, + rrIntervals: MutableList? + ) { + points.add(heartRate) + sendNewData(data.copy(heartRates = points)) + } + + private fun sendNewData(newData: HRSAggregatedData) { + data = newData + localBroadcast.offer(newData) + } +} diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HeartRateMeasurementParser.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HeartRateMeasurementParser.kt new file mode 100644 index 00000000..c69ad1c5 --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/service/HeartRateMeasurementParser.kt @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.android.hrs.service + +import no.nordicsemi.android.ble.data.Data +import java.util.* + +object HeartRateMeasurementParser { + + private const val HEART_RATE_VALUE_FORMAT: Byte = 0x01 // 1 bit + private const val SENSOR_CONTACT_STATUS: Byte = 0x06 // 2 bits + private const val ENERGY_EXPANDED_STATUS: Byte = 0x08 // 1 bit + private const val RR_INTERVAL: Byte = 0x10 // 1 bit + + fun parse(data: Data): String { + var offset = 0 + val flags = data.getIntValue(Data.FORMAT_UINT8, offset++)!! + + /* + * false Heart Rate Value Format is set to UINT8. Units: beats per minute (bpm) + * true Heart Rate Value Format is set to UINT16. Units: beats per minute (bpm) + */ + val value16bit = flags and HEART_RATE_VALUE_FORMAT.toInt() > 0 + + /* + * 0 Sensor Contact feature is not supported in the current connection + * 1 Sensor Contact feature is not supported in the current connection + * 2 Sensor Contact feature is supported, but contact is not detected + * 3 Sensor Contact feature is supported and contact is detected + */ + val sensorContactStatus = flags and SENSOR_CONTACT_STATUS.toInt() shr 1 + + /* + * false Energy Expended field is not present + * true Energy Expended field is present. Units: kilo Joules + */ + val energyExpandedStatus = flags and ENERGY_EXPANDED_STATUS.toInt() > 0 + + /* + * false RR-Interval values are not present. + * true One or more RR-Interval values are present. Units: 1/1024 seconds + */ + val rrIntervalStatus = flags and RR_INTERVAL.toInt() > 0 + + // heart rate value is 8 or 16 bit long + val heartRateValue = data.getIntValue( + if (value16bit) { + Data.FORMAT_UINT16 + } else { + Data.FORMAT_UINT8 + }, + offset++ + ) // bits per minute + if (value16bit) offset++ + + // energy expanded value is present if a flag was set + var energyExpanded = -1 + if (energyExpandedStatus) energyExpanded = data.getIntValue(Data.FORMAT_UINT16, offset)!! + offset += 2 + + // RR-interval is set when a flag is set + val rrIntervals: MutableList = ArrayList() + if (rrIntervalStatus) { + var o = offset + while (o < data.value!!.size) { + val units = data.getIntValue(Data.FORMAT_UINT16, o)!! + rrIntervals.add(units * 1000.0f / 1024.0f) // RR interval is in [1/1024s] + o += 2 + } + } + val builder = StringBuilder() + builder.append("Heart Rate Measurement: ").append(heartRateValue).append(" bpm") + when (sensorContactStatus) { + 0, 1 -> builder.append(",\nSensor Contact Not Supported") + 2 -> builder.append(",\nContact is NOT Detected") + 3 -> builder.append(",\nContact is Detected") + } + if (energyExpandedStatus) { + builder.append(",\nEnergy Expanded: ") + .append(energyExpanded) + .append(" kJ") + } + if (rrIntervalStatus) { + builder.append(",\nRR Interval: ") + for (interval in rrIntervals) builder.append( + String.format( + Locale.US, + "%.02f ms, ", + interval + ) + ) + builder.setLength(builder.length - 2) // remove the ", " at the end + } + return builder.toString() + } +} \ No newline at end of file diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/ContentView.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/ContentView.kt new file mode 100644 index 00000000..9ee0c88c --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/ContentView.kt @@ -0,0 +1,224 @@ +package no.nordicsemi.android.hrs.view + +import android.content.Context +import android.graphics.Color +import android.graphics.DashPathEffect +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Card +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.content.ContextCompat +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.formatter.IFillFormatter +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.github.mikephil.charting.utils.Utils +import no.nordicsemi.android.hrs.R +import no.nordicsemi.android.hrs.viewmodel.HRSViewState +import no.nordicsemi.android.theme.NordicColors +import no.nordicsemi.android.theme.view.BatteryLevelView +import java.util.* + +@Composable +internal fun ContentView(state: HRSViewState, onEvent: (HRSScreenViewEvent) -> Unit) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Card( + backgroundColor = NordicColors.NordicGray4.value(), + shape = RoundedCornerShape(10.dp), + elevation = 0.dp + ) { + Box(modifier = Modifier.padding(16.dp)) { + LineChartView(state) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + BatteryLevelView(state.batteryLevel) + + Spacer(modifier = Modifier.height(16.dp)) + + Button( + colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.secondary), + onClick = { onEvent(DisconnectEvent) } + ) { + Text(text = stringResource(id = R.string.disconnect)) + } + } +} + +@Composable +fun LineChartView(state: HRSViewState) { + AndroidView( + modifier = Modifier + .fillMaxWidth() + .height(300.dp), + factory = { createLineChartView(it, state) }, + update = { updateData(state.points, it) } + ) +} + +fun createLineChartView(context: Context, state: HRSViewState): LineChart { + return LineChart(context).apply { + setBackgroundColor(Color.WHITE) + + description.isEnabled = false + + setTouchEnabled(true) + +// setOnChartValueSelectedListener(this) + setDrawGridBackground(false) + + isDragEnabled = true + setScaleEnabled(true) + setPinchZoom(true) + + xAxis.apply { + xAxis.enableGridDashedLine(10f, 10f, 0f) + } + axisLeft.apply { + enableGridDashedLine(10f, 10f, 0f) + + axisMaximum = 300f + axisMinimum = 100f + } + axisRight.isEnabled = false + + //--- + + val entries = state.points.mapIndexed { i, v -> + Entry(i.toFloat(), v.toFloat()) + } + // create a dataset and give it a type + + if (data != null && data.dataSetCount > 0) { + val set1 = data!!.getDataSetByIndex(0) as LineDataSet + set1.values = entries + set1.notifyDataSetChanged() + data!!.notifyDataChanged() + notifyDataSetChanged() + } else { + val set1 = LineDataSet(entries, "DataSet 1") + + set1.setDrawIcons(false) + + // draw dashed line + + // draw dashed line + set1.enableDashedLine(10f, 5f, 0f) + + // black lines and points + + // black lines and points + set1.color = Color.BLACK + set1.setCircleColor(Color.BLACK) + + // line thickness and point size + + // line thickness and point size + set1.lineWidth = 1f + set1.circleRadius = 3f + + // draw points as solid circles + + // draw points as solid circles + set1.setDrawCircleHole(false) + + // customize legend entry + + // customize legend entry + set1.formLineWidth = 1f + set1.formLineDashEffect = DashPathEffect(floatArrayOf(10f, 5f), 0f) + set1.formSize = 15f + + // text size of values + + // text size of values + set1.valueTextSize = 9f + + // draw selection line as dashed + + // draw selection line as dashed + set1.enableDashedHighlightLine(10f, 5f, 0f) + + // set the filled area + + // set the filled area + set1.setDrawFilled(true) + set1.fillFormatter = IFillFormatter { _, _ -> + axisLeft.axisMinimum + } + + // set color of filled area + + // set color of filled area + if (Utils.getSDKInt() >= 18) { + // drawables only supported on api level 18 and above + val drawable = ContextCompat.getDrawable(context, R.drawable.fade_red) + set1.fillDrawable = drawable + } else { + set1.fillColor = Color.BLACK + } + + val dataSets = ArrayList() + dataSets.add(set1) // add the data sets + + + // create a data object with the data sets + + // create a data object with the data sets + val data = LineData(dataSets) + + // set data + + // set data + setData(data) + } + } +} + +private fun updateData(points: List, chart: LineChart) { + val entries = points.mapIndexed { i, v -> + Entry(i.toFloat(), v.toFloat()) + } + + with(chart) { + if (data != null && data.dataSetCount > 0) { + val set1 = data!!.getDataSetByIndex(0) as LineDataSet + set1.values = entries + set1.notifyDataSetChanged() + data!!.notifyDataChanged() + notifyDataSetChanged() + invalidate() + } + } +} + +@Preview +@Composable +private fun Preview() { + ContentView(state = HRSViewState()) { } +} diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt new file mode 100644 index 00000000..a6dc4d2a --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -0,0 +1,52 @@ +package no.nordicsemi.android.hrs.view + +import android.content.Intent +import androidx.compose.foundation.layout.Column +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel +import no.nordicsemi.android.hrs.R +import no.nordicsemi.android.hrs.service.HRSService +import no.nordicsemi.android.hrs.viewmodel.HRSViewModel +import no.nordicsemi.android.hrs.viewmodel.HRSViewState +import no.nordicsemi.android.utils.isServiceRunning + +@Composable +fun HRSScreen(finishAction: () -> Unit) { + val viewModel: HRSViewModel = hiltViewModel() + val state = viewModel.state.collectAsState().value + + val context = LocalContext.current + LaunchedEffect(state.isScreenActive) { + if (!state.isScreenActive) { + finishAction() + } + if (context.isServiceRunning(HRSService::class.java.name)) { + val intent = Intent(context, HRSService::class.java) + context.stopService(intent) + } + } + + LaunchedEffect("start-service") { + if (!context.isServiceRunning(HRSService::class.java.name)) { + val intent = Intent(context, HRSService::class.java) + context.startService(intent) + } + } + + HRSView(state) { viewModel.onEvent(it) } +} + +@Composable +private fun HRSView(state: HRSViewState, onEvent: (HRSScreenViewEvent) -> Unit) { + Column { + TopAppBar(title = { Text(text = stringResource(id = R.string.hrs_title)) }) + + ContentView(state) { onEvent(it) } + } +} diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreenViewEvent.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreenViewEvent.kt new file mode 100644 index 00000000..07ca835b --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreenViewEvent.kt @@ -0,0 +1,5 @@ +package no.nordicsemi.android.hrs.view + +sealed class HRSScreenViewEvent + +object DisconnectEvent : HRSScreenViewEvent() diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt new file mode 100644 index 00000000..cd0a4aee --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -0,0 +1,47 @@ +package no.nordicsemi.android.hrs.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.withContext +import no.nordicsemi.android.hrs.events.HRSAggregatedData +import no.nordicsemi.android.hrs.service.HRSDataBroadcast +import no.nordicsemi.android.hrs.view.DisconnectEvent +import no.nordicsemi.android.hrs.view.HRSScreenViewEvent +import javax.inject.Inject + +@HiltViewModel +internal class HRSViewModel @Inject constructor( + private val localBroadcast: HRSDataBroadcast +) : ViewModel() { + + val state = MutableStateFlow(HRSViewState()) + + init { + localBroadcast.events.onEach { + withContext(Dispatchers.Main) { consumeEvent(it) } + }.launchIn(viewModelScope) + } + + private fun consumeEvent(event: HRSAggregatedData) { + state.value = state.value.copy( + points = event.heartRates, + batteryLevel = event.batteryLevel, + sensorLocation = event.sensorLocation + ) + } + + fun onEvent(event: HRSScreenViewEvent) { + (event as? DisconnectEvent)?.let { + onDisconnectButtonClick() + } + } + + private fun onDisconnectButtonClick() { + state.tryEmit(state.value.copy(isScreenActive = false)) + } +} diff --git a/feature_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewState.kt b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewState.kt new file mode 100644 index 00000000..75ef9d94 --- /dev/null +++ b/feature_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewState.kt @@ -0,0 +1,8 @@ +package no.nordicsemi.android.hrs.viewmodel + +data class HRSViewState( + val points: List = listOf(1, 2, 3), + val batteryLevel: Int = 0, + val sensorLocation: Int = 0, + val isScreenActive: Boolean = true +) diff --git a/feature_hrs/src/main/res/drawable/fade_red.xml b/feature_hrs/src/main/res/drawable/fade_red.xml new file mode 100644 index 00000000..54ac10ba --- /dev/null +++ b/feature_hrs/src/main/res/drawable/fade_red.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/feature_hrs/src/main/res/values/strings.xml b/feature_hrs/src/main/res/values/strings.xml new file mode 100644 index 00000000..6c7eee4d --- /dev/null +++ b/feature_hrs/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + HRS + diff --git a/feature_hrs/src/test/java/no/nordicsemi/android/hrs/ExampleUnitTest.kt b/feature_hrs/src/test/java/no/nordicsemi/android/hrs/ExampleUnitTest.kt new file mode 100644 index 00000000..51644e23 --- /dev/null +++ b/feature_hrs/src/test/java/no/nordicsemi/android/hrs/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package no.nordicsemi.android.hrs + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature_scanner/build.gradle b/feature_scanner/build.gradle index b29a2804..e1151501 100644 --- a/feature_scanner/build.gradle +++ b/feature_scanner/build.gradle @@ -4,6 +4,7 @@ apply plugin: 'kotlin-parcelize' dependencies { implementation project(":lib_utils") implementation project(":lib_theme") + implementation project(":lib_service") implementation libs.material implementation libs.google.permissions diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/HiltModule.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/HiltModule.kt index 6b906bd9..02216cab 100644 --- a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/HiltModule.kt +++ b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/HiltModule.kt @@ -7,7 +7,8 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent -import no.nordicsemi.android.scanner.tools.SelectedBluetoothDeviceHolder +import no.nordicsemi.android.scanner.tools.PermissionHelper +import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder import javax.inject.Singleton @Module @@ -24,7 +25,16 @@ internal object HiltModule { fun createSelectedBluetoothDeviceHolder( @ApplicationContext context: Context, bluetoothAdapter: BluetoothAdapter? - ): SelectedBluetoothDeviceHolder { - return SelectedBluetoothDeviceHolder(context, bluetoothAdapter) + ): no.nordicsemi.android.service.SelectedBluetoothDeviceHolder { + return no.nordicsemi.android.service.SelectedBluetoothDeviceHolder( + context, + bluetoothAdapter + ) + } + + @Singleton + @Provides + fun createPermissionHelper(@ApplicationContext context: Context): PermissionHelper { + return PermissionHelper(context) } } diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/ScannerNavigation.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/ScannerNavigation.kt deleted file mode 100644 index eecf29a4..00000000 --- a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/ScannerNavigation.kt +++ /dev/null @@ -1,41 +0,0 @@ -package no.nordicsemi.android.scanner - -import androidx.compose.foundation.layout.Column -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.res.stringResource -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavController -import no.nordicsemi.android.scanner.tools.ScannerStatus -import no.nordicsemi.android.scanner.view.* -import no.nordicsemi.android.scanner.viewmodel.NordicBleScannerViewModel -import no.nordicsemi.android.scanner.viewmodel.ScannerViewEvent -import no.nordicsemi.android.utils.exhaustive - -@Composable -fun ScannerRoute(navController: NavController) { - val viewModel = hiltViewModel() - - val scannerStatus = viewModel.state.collectAsState().value - - Column { - TopAppBar(title = { Text(text = stringResource(id = R.string.scanner__devices_list)) }) - ScannerScreen(navController, scannerStatus) { viewModel.onEvent(it) } - } -} - -@Composable -private fun ScannerScreen( - navController: NavController, - scannerStatus: ScannerStatus, - onEvent: (ScannerViewEvent) -> Unit -) { - when (scannerStatus) { - ScannerStatus.PERMISSION_REQUIRED -> RequestPermissionScreen { onEvent(ScannerViewEvent.PERMISSION_CHECKED) } - ScannerStatus.NOT_AVAILABLE -> BluetoothNotAvailableScreen() - ScannerStatus.DISABLED -> BluetoothNotEnabledScreen { onEvent(ScannerViewEvent.BLUETOOTH_ENABLED) } - ScannerStatus.ENABLED -> ScanDeviceScreen(navController) - }.exhaustive -} diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/NordicBleScanner.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/NordicBleScanner.kt index 4ec1ad37..b4bd5183 100644 --- a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/NordicBleScanner.kt +++ b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/NordicBleScanner.kt @@ -2,14 +2,10 @@ package no.nordicsemi.android.scanner.tools import android.annotation.SuppressLint import android.bluetooth.BluetoothAdapter -import android.bluetooth.BluetoothDevice -import kotlinx.coroutines.flow.MutableStateFlow import javax.inject.Inject @SuppressLint("MissingPermission") -internal class NordicBleScanner @Inject constructor(private val bleAdapter: BluetoothAdapter?) { - - val scannerResult = MutableStateFlow(DeviceListResult()) +class NordicBleScanner @Inject constructor(private val bleAdapter: BluetoothAdapter?) { fun getBluetoothStatus(): ScannerStatus { return when { @@ -19,15 +15,3 @@ internal class NordicBleScanner @Inject constructor(private val bleAdapter: Blue } } } - -sealed class ScanningResult - -data class DeviceListResult(val devices: List = emptyList()) : ScanningResult() - -object ScanningErrorResult : ScanningResult() - -private fun MutableList.addIfNotExist(value: T) { - if (!contains(value)) { - add(value) - } -} diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/PermissionHelper.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/PermissionHelper.kt new file mode 100644 index 00000000..f96447c7 --- /dev/null +++ b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/PermissionHelper.kt @@ -0,0 +1,21 @@ +package no.nordicsemi.android.scanner.tools + +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import androidx.core.content.ContextCompat + +class PermissionHelper(private val context: Context) { + + fun isRequiredPermissionGranted(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + ContextCompat.checkSelfPermission( + context, + Manifest.permission.BLUETOOTH_CONNECT + ) == PackageManager.PERMISSION_GRANTED + } else { + true + } + } +} diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/ScannerStatus.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/ScannerStatus.kt index e08da090..4c884cab 100644 --- a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/ScannerStatus.kt +++ b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/tools/ScannerStatus.kt @@ -1,5 +1,5 @@ package no.nordicsemi.android.scanner.tools -internal enum class ScannerStatus { - PERMISSION_REQUIRED, ENABLED, DISABLED, NOT_AVAILABLE +enum class ScannerStatus { + ENABLED, DISABLED, NOT_AVAILABLE } diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/BluetoothNotAvailableScreen.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/BluetoothNotAvailableScreen.kt index 503bcc97..1756e495 100644 --- a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/BluetoothNotAvailableScreen.kt +++ b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/BluetoothNotAvailableScreen.kt @@ -5,18 +5,42 @@ import android.bluetooth.BluetoothAdapter import android.content.Intent import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme import androidx.compose.material.Text +import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.scanner.R @Composable -internal fun BluetoothNotAvailableScreen() { - Text("Bluetooth not available.") +fun BluetoothNotAvailableScreen() { + Column { + TopAppBar(title = { Text(text = stringResource(id = R.string.scanner__request_permission)) }) + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(stringResource(R.string.scanner__bluetooth_not_available)) + } + } } @Composable -internal fun BluetoothNotEnabledScreen(finish: () -> Unit) { +fun BluetoothNotEnabledScreen(finish: () -> Unit) { val contract = ActivityResultContracts.StartActivityForResult() val launcher = rememberLauncherForActivityResult(contract = contract, onResult = { if (it.resultCode == Activity.RESULT_OK) { @@ -25,10 +49,30 @@ internal fun BluetoothNotEnabledScreen(finish: () -> Unit) { }) Column { - Text(text = "Bluetooth not enabled.") - Text(text = "To enable Bluetooth please open settings.") - Button(onClick = { launcher.launch(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) }) { - Text(text = "Bluetooth not available.") + TopAppBar(title = { Text(text = stringResource(id = R.string.scanner__request_permission)) }) + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + textAlign = TextAlign.Center, + text = stringResource(id = R.string.scanner__bluetooth_not_enabled) + ) + Spacer(Modifier.height(16.dp)) + Text( + textAlign = TextAlign.Center, + text = stringResource(id = R.string.scanner__bluetooth_open_settings_info) + ) + Spacer(Modifier.height(32.dp)) + Button( + colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.secondary), + onClick = { launcher.launch(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)) } + ) { + Text(text = stringResource(id = R.string.scanner__bluetooth_open_settings)) + } } } } diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/NotConnectedView.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/NotConnectedView.kt new file mode 100644 index 00000000..f3790a3b --- /dev/null +++ b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/NotConnectedView.kt @@ -0,0 +1,54 @@ +package no.nordicsemi.android.scanner.view + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.scanner.R + +@Composable +private fun NotConnectedScreen( + connect: () -> Unit +) { + NotConnectedView(connect) +} + +@Composable +private fun NotConnectedView( + connect: () -> Unit +) { + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(text = stringResource(id = R.string.csc_no_connection)) + Spacer(modifier = Modifier.height(16.dp)) + Button( + colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.secondary), + onClick = { connect() } + ) { + Text(text = stringResource(id = R.string.csc_connect)) + } + } +} + +@Preview +@Composable +private fun NotConnectedPreview() { + NotConnectedView { } +} diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/RequestPermissionScreen.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/RequestPermissionScreen.kt index 75da5753..13552db1 100644 --- a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/RequestPermissionScreen.kt +++ b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/RequestPermissionScreen.kt @@ -4,9 +4,18 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.provider.Settings -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.Button import androidx.compose.material.Text +import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable @@ -14,6 +23,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat.startActivity @@ -24,18 +34,21 @@ import no.nordicsemi.android.scanner.R @OptIn(ExperimentalPermissionsApi::class) @Composable -internal fun RequestPermissionScreen(finish: () -> Unit) { +fun RequestPermissionScreen(finish: () -> Unit) { val permissionsState = rememberMultiplePermissionsState(listOf( - android.Manifest.permission.ACCESS_FINE_LOCATION, -// android.Manifest.permission.BLUETOOTH_SCAN, -// android.Manifest.permission.BLUETOOTH_CONNECT + android.Manifest.permission.BLUETOOTH_CONNECT )) - PermissionsRequired( - multiplePermissionsState = permissionsState, - permissionsNotGrantedContent = { PermissionNotGranted { permissionsState.launchMultiplePermissionRequest() } }, - permissionsNotAvailableContent = { PermissionNotAvailable() } - ) { - finish() + + Column { + TopAppBar(title = { Text(text = stringResource(id = R.string.scanner__request_permission)) }) + + PermissionsRequired( + multiplePermissionsState = permissionsState, + permissionsNotGrantedContent = { PermissionNotGranted { permissionsState.launchMultiplePermissionRequest() } }, + permissionsNotAvailableContent = { PermissionNotAvailable() } + ) { + finish() + } } } @@ -45,7 +58,9 @@ private fun PermissionNotGranted(onClick: () -> Unit) { if (doNotShowRationale.value) { Column( - modifier = Modifier.fillMaxWidth().fillMaxHeight(), + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { @@ -53,18 +68,21 @@ private fun PermissionNotGranted(onClick: () -> Unit) { } } else { Column( - modifier = Modifier.fillMaxWidth().fillMaxHeight(), + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + .padding(16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - Text(stringResource(id = R.string.scanner__permission_rationale)) - Spacer(modifier = Modifier.height(8.dp)) + Text(textAlign = TextAlign.Center, text = stringResource(id = R.string.scanner__permission_rationale)) + Spacer(modifier = Modifier.height(16.dp)) Row { - Button(onClick = { onClick() }) { + Button(modifier = Modifier.width(100.dp), onClick = { onClick() }) { Text(stringResource(id = R.string.scanner__button_ok)) } - Spacer(Modifier.width(8.dp)) - Button(onClick = { doNotShowRationale.value = true }) { + Spacer(Modifier.width(16.dp)) + Button(modifier = Modifier.width(100.dp), onClick = { doNotShowRationale.value = true }) { Text(stringResource(id = R.string.scanner__button_nope)) } } @@ -76,7 +94,9 @@ private fun PermissionNotGranted(onClick: () -> Unit) { private fun PermissionNotAvailable() { val context = LocalContext.current Column( - modifier = Modifier.fillMaxWidth().fillMaxHeight(), + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/ScanDeviceScreen.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/ScanDeviceScreen.kt index 4ddc7084..add8df59 100644 --- a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/ScanDeviceScreen.kt +++ b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/view/ScanDeviceScreen.kt @@ -1,14 +1,11 @@ package no.nordicsemi.android.scanner.view import android.app.Activity -import android.bluetooth.BluetoothDevice -import android.bluetooth.le.ScanResult import android.companion.AssociationRequest import android.companion.BluetoothLeDeviceFilter import android.companion.CompanionDeviceManager import android.content.Context import android.content.IntentSender -import android.os.Build import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.IntentSenderRequest import androidx.activity.result.contract.ActivityResultContracts @@ -16,54 +13,48 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import androidx.navigation.NavController @Composable -fun ScanDeviceScreen(navController: NavController,) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val deviceFilter = BluetoothLeDeviceFilter.Builder() - .build() +fun ScanDeviceScreen(finish: (ScanDeviceScreenResult) -> Unit) { + val deviceManager = + LocalContext.current.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager - val pairingRequest: AssociationRequest = AssociationRequest.Builder() - .addDeviceFilter(deviceFilter) - .build() - - val deviceManager = - LocalContext.current.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager - - val contract = ActivityResultContracts.StartIntentSenderForResult() - val launcher = rememberLauncherForActivityResult(contract = contract, onResult = { - if (it.resultCode == Activity.RESULT_OK) { - //Sometimes result is ScanResult & sometimes BluetoothDevice - val device: BluetoothDevice = try { - it.data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)!! - } catch (e: Exception) { - (it.data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE))!!.device - } - - navController.previousBackStackEntry - ?.savedStateHandle - ?.set("result", device) - } - navController.popBackStack() - }) - - val hasBeenInvoked = remember { mutableStateOf(false) } - if (hasBeenInvoked.value) { - return + val contract = ActivityResultContracts.StartIntentSenderForResult() + val launcher = rememberLauncherForActivityResult(contract = contract) { + val result = if (it.resultCode == Activity.RESULT_OK) { + ScanDeviceScreenResult.SUCCESS + } else { + ScanDeviceScreenResult.CANCEL } - hasBeenInvoked.value = true - deviceManager.associate(pairingRequest, - object : CompanionDeviceManager.Callback() { - override fun onDeviceFound(chooserLauncher: IntentSender) { - val request = IntentSenderRequest.Builder(chooserLauncher).build() - launcher.launch(request) - } - - override fun onFailure(error: CharSequence?) { - } - }, null) - } else { - TODO("VERSION.SDK_INT < O") + finish(result) } -} \ No newline at end of file + + val hasBeenInvoked = remember { mutableStateOf(false) } + if (hasBeenInvoked.value) { + return + } + hasBeenInvoked.value = true + + val deviceFilter = BluetoothLeDeviceFilter.Builder() + .build() + + val pairingRequest: AssociationRequest = AssociationRequest.Builder() + .addDeviceFilter(deviceFilter) + .build() + + deviceManager.associate(pairingRequest, + object : CompanionDeviceManager.Callback() { + override fun onDeviceFound(chooserLauncher: IntentSender) { + val request = IntentSenderRequest.Builder(chooserLauncher).build() + launcher.launch(request) + } + + override fun onFailure(error: CharSequence?) { + } + }, null + ) +} + +enum class ScanDeviceScreenResult { + SUCCESS, CANCEL +} diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/viewmodel/BluetoothPermissionState.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/viewmodel/BluetoothPermissionState.kt new file mode 100644 index 00000000..6baab097 --- /dev/null +++ b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/viewmodel/BluetoothPermissionState.kt @@ -0,0 +1,9 @@ +package no.nordicsemi.android.scanner.viewmodel + +enum class BluetoothPermissionState { + PERMISSION_REQUIRED, + BLUETOOTH_NOT_AVAILABLE, + BLUETOOTH_NOT_ENABLED, + DEVICE_NOT_CONNECTED, + READY +} diff --git a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/viewmodel/NordicBleScannerViewModel.kt b/feature_scanner/src/main/java/no/nordicsemi/android/scanner/viewmodel/NordicBleScannerViewModel.kt deleted file mode 100644 index 49f9d6fa..00000000 --- a/feature_scanner/src/main/java/no/nordicsemi/android/scanner/viewmodel/NordicBleScannerViewModel.kt +++ /dev/null @@ -1,37 +0,0 @@ -package no.nordicsemi.android.scanner.viewmodel - -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import no.nordicsemi.android.scanner.tools.NordicBleScanner -import no.nordicsemi.android.scanner.tools.ScannerStatus -import no.nordicsemi.android.utils.exhaustive -import javax.inject.Inject - -@HiltViewModel -internal class NordicBleScannerViewModel @Inject constructor( - private val bleScanner: NordicBleScanner -) : ViewModel() { - - val state = - MutableStateFlow(ScannerStatus.PERMISSION_REQUIRED) - - fun onEvent(event: ScannerViewEvent) { - when (event) { - ScannerViewEvent.PERMISSION_CHECKED -> onPermissionChecked() - ScannerViewEvent.BLUETOOTH_ENABLED -> onBluetoothEnabled() - }.exhaustive - } - - private fun onPermissionChecked() { - state.value = bleScanner.getBluetoothStatus() - } - - private fun onBluetoothEnabled() { - state.value = bleScanner.getBluetoothStatus() - } -} - -internal enum class ScannerViewEvent { - PERMISSION_CHECKED, BLUETOOTH_ENABLED -} diff --git a/feature_scanner/src/main/res/values/strings.xml b/feature_scanner/src/main/res/values/strings.xml index 409e94e0..a12e9a62 100644 --- a/feature_scanner/src/main/res/values/strings.xml +++ b/feature_scanner/src/main/res/values/strings.xml @@ -4,12 +4,23 @@ The location permission is required when using Bluetooth LE, because surrounding devices can expose user\'s location. Please grant the permission. Location permission denied. Please, grant us access on the Settings screen. - OK - Nope Open settings Feature not available List of devices Scanning failed due to technical reason. Name: NONE + + No device connected + Connect + + Grant + Deny + + Request permission + + Bluetooth not available. + Bluetooth not enabled. + To enable Bluetooth please open settings. + Open settings diff --git a/lib_service/build.gradle b/lib_service/build.gradle index 51af2af3..0fb97a02 100644 --- a/lib_service/build.gradle +++ b/lib_service/build.gradle @@ -2,7 +2,7 @@ apply from: rootProject.file("library.gradle") apply plugin: 'kotlin-parcelize' dependencies { - implementation project(":feature_scanner") + implementation project(":lib_theme") implementation libs.nordic.ble.common implementation libs.nordic.log diff --git a/lib_service/src/main/AndroidManifest.xml b/lib_service/src/main/AndroidManifest.xml index c2a57b13..ea6c2307 100644 --- a/lib_service/src/main/AndroidManifest.xml +++ b/lib_service/src/main/AndroidManifest.xml @@ -2,4 +2,7 @@ + + + \ No newline at end of file diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/BleProfileService.kt b/lib_service/src/main/java/no/nordicsemi/android/service/BleProfileService.kt index 722b89bb..6f5440d8 100644 --- a/lib_service/src/main/java/no/nordicsemi/android/service/BleProfileService.kt +++ b/lib_service/src/main/java/no/nordicsemi/android/service/BleProfileService.kt @@ -42,7 +42,6 @@ import no.nordicsemi.android.ble.BleManagerCallbacks import no.nordicsemi.android.ble.utils.ILogger import no.nordicsemi.android.log.ILogSession import no.nordicsemi.android.log.Logger -import no.nordicsemi.android.scanner.tools.SelectedBluetoothDeviceHolder import javax.inject.Inject @AndroidEntryPoint @@ -68,7 +67,7 @@ abstract class BleProfileService : LifecycleService(), BleManagerCallbacks { * @return bluetooth device */ protected val bluetoothDevice: BluetoothDevice by lazy { - bluetoothDeviceHolder.device ?: throw UnsupportedOperationException( + bluetoothDeviceHolder.device ?: throw IllegalArgumentException( "No device address at EXTRA_DEVICE_ADDRESS key" ) } diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/ForegroundBleService.kt b/lib_service/src/main/java/no/nordicsemi/android/service/ForegroundBleService.kt index 9e25f020..5422ace3 100644 --- a/lib_service/src/main/java/no/nordicsemi/android/service/ForegroundBleService.kt +++ b/lib_service/src/main/java/no/nordicsemi/android/service/ForegroundBleService.kt @@ -22,16 +22,29 @@ package no.nordicsemi.android.service import android.app.Notification +import android.app.NotificationChannel import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Intent import android.os.Build +import androidx.core.app.NotificationCompat + +private const val CHANNEL_ID = "FOREGROUND_BLE_SERVICE" abstract class ForegroundBleService> : BleProfileService() { protected abstract val manager: T + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + val result = super.onStartCommand(intent, flags, startId) + startForegroundService() + return result + } + override fun onDestroy() { // when user has disconnected from the sensor, we have to cancel the notification that we've created some milliseconds before using unbindService cancelNotification() + stopForegroundService() super.onDestroy() } @@ -87,24 +100,30 @@ abstract class ForegroundBleServiceBonding with the device… The device is now bonded. %s is connected. + Background connections + Shows a notification when a device is connected in background. diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/Background.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/Background.kt deleted file mode 100644 index 91797fe9..00000000 --- a/lib_theme/src/main/java/no/nordicsemi/android/theme/Background.kt +++ /dev/null @@ -1,24 +0,0 @@ -package no.nordicsemi.android.theme - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp - -object Background { - - @Composable - fun whiteRoundedCorners(): Modifier { - return Modifier - .background(Color(0xffffffff)) - .padding(16.dp) - .verticalScroll(rememberScrollState()) - .clip(RoundedCornerShape(10.dp)) - } -} \ No newline at end of file diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/Color.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/Color.kt index 2bfd9030..82bbd901 100644 --- a/lib_theme/src/main/java/no/nordicsemi/android/theme/Color.kt +++ b/lib_theme/src/main/java/no/nordicsemi/android/theme/Color.kt @@ -1,26 +1,67 @@ package no.nordicsemi.android.theme +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color object NordicColors { - val Primary = Color(0xFF00A9CE) - val PrimaryLight = Color(0xFF5fdbff) - val PrimaryDark = Color(0xFF007a9d) - val Secondary = Color(0xFF0077c8) - val SecondaryLight = Color(0xFF57c0e2) - val SecondaryDark = Color(0xFF004c97) - val Text = Color(0xFF00A9CE) - + val AlmostWhite = Color(0xFFDADADA) val NordicBlue = Color(0xFF00A9CE) - val NordicBlueDark = Color(0xFF0090B0) - val NordicSky = Color(0xFF6AD1E3) - val NordicBlueLate = Color(0xFF0033A0) - val NordicLake = Color(0xFF0077C8) - val NordicLightGray = Color(0xFFD9E1E2) - val NordicMediumGray = Color(0xFF768692) - val NordicDarkGray = Color(0xFF333F48) - val NordicGrass = Color(0xFFD0DF00) - val NordicSun = Color(0xFFFFCD00) - val NordicRed = Color(0xFFEE2F4E) - val NordicFall = Color(0xFFF58220) + val NordicLake = Color(0xFF008CD2) + + val NordicDarkGray = ThemedColor(Color(0xFF333F48), Color(0xFFCCCBC8)) + +// val NordicGray4 = ThemedColor(Color(0xFFD1D1D6), Color(0xFF3A3A3C)) + val NordicGray4 = ThemedColor(Color.White, Color(0xFF3A3A3C)) + + val NordicGray5 = ThemedColor(Color(0xFFE5E5EA), Color(0xFF2C2C2E)) + val NordicLightGray = NeutralColor(Color(0xFF929CA2)) + val NordicMediumGray = NeutralColor(Color(0xFF929CA2)) + + val NordicFall = ThemedColor(Color(0xFFF99535), Color(0xFFFF9F0A)) + val NordicGreen = ThemedColor(Color(0xFF3ED052), Color(0xFF32D74B)) + + val NordicOrange = ThemedColor(Color(0xFFDF9B16), Color(0xFFFF9F0A)) + val NordicRed = ThemedColor(Color(0xFFD03E51), Color(0xFFFF453A)) + val NordicSky = NeutralColor(Color(0xFF6AD1E3)) + val NordicYellow = ThemedColor(Color(0xFFF9EE35), Color(0xFFFFD60A)) + val TableViewBackground = NeutralColor(Color(0xFFF2F2F6)) + val TableViewSeparator = NeutralColor(Color(0xFFD2D2D6)) + + val Primary = ThemedColor(Color(0xFF00A9CE), Color(0xFF212121)) + val PrimaryVariant = ThemedColor(Color(0xFF008CD2), Color.Black) + val Secondary = ThemedColor(Color(0xFF00A9CE), Color(0xFF008CD2)) + val SecondaryVariant = ThemedColor(Color(0xFF008CD2), Color(0xFF008CD2)) + val OnPrimary = ThemedColor(Color.White, Color.White) + val OnSecondary = ThemedColor(Color.White, Color.White) + val OnBackground = ThemedColor(Color.Black, Color.White) + val OnSurface = ThemedColor(Color.Black, Color.White) + val Background = ThemedColor(Color(0xFFDADADA), Color.Black) + val Surface = ThemedColor(Color(0xFFDADADA), Color.Black) +} + +sealed class NordicColor { + + @Composable + abstract fun value(): Color +} + +data class ThemedColor(val light: Color, val dark: Color): NordicColor() { + + @Composable + override fun value(): Color { + return if (isSystemInDarkTheme()) { + dark + } else { + light + } + } +} + +data class NeutralColor(val color: Color): NordicColor() { + + @Composable + override fun value(): Color { + return color + } } diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/Theme.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/Theme.kt index 0620c362..f8fa424e 100644 --- a/lib_theme/src/main/java/no/nordicsemi/android/theme/Theme.kt +++ b/lib_theme/src/main/java/no/nordicsemi/android/theme/Theme.kt @@ -5,41 +5,40 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.darkColors import androidx.compose.material.lightColors import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color - -//TODO -private val DarkColorPalette = darkColors( - primary = NordicColors.Primary, - primaryVariant = NordicColors.PrimaryDark, - secondary = NordicColors.Secondary, - secondaryVariant = NordicColors.SecondaryDark, - onSecondary = Color.White, - onPrimary = Color.White, - onBackground = Color.Black, - onSurface = Color.Black, - background = Color.White, - surface = Color.White, -) - -private val LightColorPalette = lightColors( - primary = NordicColors.Primary, - primaryVariant = NordicColors.PrimaryDark, - secondary = NordicColors.Secondary, - secondaryVariant = NordicColors.SecondaryDark, - onSecondary = Color.White, - onPrimary = Color.White, - onBackground = Color.Black, - onSurface = Color.Black, - background = Color.White, - surface = Color.White, -) @Composable fun TestTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) { + + val darkColorPalette = darkColors( + primary = NordicColors.Primary.value(), + primaryVariant = NordicColors.PrimaryVariant.value(), + secondary = NordicColors.Secondary.value(), + secondaryVariant = NordicColors.SecondaryVariant.value(), + onSecondary = NordicColors.OnSecondary.value(), + onPrimary = NordicColors.OnPrimary.value(), + onBackground = NordicColors.OnBackground.value(), + onSurface = NordicColors.OnSurface.value(), + background = NordicColors.Background.value(), + surface = NordicColors.Surface.value(), + ) + + val lightColorPalette = lightColors( + primary = NordicColors.Primary.value(), + primaryVariant = NordicColors.PrimaryVariant.value(), + secondary = NordicColors.Secondary.value(), + secondaryVariant = NordicColors.SecondaryVariant.value(), + onSecondary = NordicColors.OnSecondary.value(), + onPrimary = NordicColors.OnPrimary.value(), + onBackground = NordicColors.OnBackground.value(), + onSurface = NordicColors.OnSurface.value(), + background = NordicColors.Background.value(), + surface = NordicColors.Surface.value(), + ) + val colors = if (darkTheme) { - DarkColorPalette + darkColorPalette } else { - LightColorPalette + lightColorPalette } MaterialTheme( diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/BatteryLevelView.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/BatteryLevelView.kt new file mode 100644 index 00000000..0a1fbd52 --- /dev/null +++ b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/BatteryLevelView.kt @@ -0,0 +1,28 @@ +package no.nordicsemi.android.theme.view + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import no.nordicsemi.android.theme.NordicColors +import no.nordicsemi.android.theme.R + +@Composable +fun BatteryLevelView(batteryLevel: Int) { + Card( + backgroundColor = NordicColors.NordicGray4.value(), + shape = RoundedCornerShape(10.dp), + elevation = 0.dp + ) { + Box(modifier = Modifier.padding(16.dp)) { + KeyValueField( + stringResource(id = R.string.field_battery), + "$batteryLevel%" + ) + } + } +} \ No newline at end of file diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/view/KeyValueField.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/KeyValueField.kt new file mode 100644 index 00000000..08e6a58a --- /dev/null +++ b/lib_theme/src/main/java/no/nordicsemi/android/theme/view/KeyValueField.kt @@ -0,0 +1,23 @@ +package no.nordicsemi.android.theme.view + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import no.nordicsemi.android.theme.NordicColors + +@Composable + fun KeyValueField(key: String, value: String) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text(text = key) + Text( + color = NordicColors.NordicDarkGray.value(), + text = value + ) + } +} diff --git a/lib_theme/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/lib_theme/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..5adfaf9b --- /dev/null +++ b/lib_theme/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/lib_theme/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/lib_theme/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..5adfaf9b --- /dev/null +++ b/lib_theme/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/lib_theme/src/main/res/mipmap-hdpi/ic_launcher.png b/lib_theme/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a6c183f85d6de9b9ddf765c3766b9876f12a45b2 GIT binary patch literal 3129 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 mJ&d-RZ>|9*z>;cu%uqS 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 O0000~8MqALnN8UD#c+>|G#felt6h z&E9+NIp6bppWp8aP7*1la)1Fq5ik~*0E`0007HSnK(TXq6L0`%0JZ}=fz3c2&K5SFzzCoakUl@c213A>z~h!>y>!|G zq?9TGW&_Ux7AKb!Fdz7*Wm(Oq6+lWU3z!R3IeO(((oSFzu-LLJb;>U?dy%u&v^i)&FYIvVtQ~XbwZJgp&NMJ?82}VTni*c-Ech(t{I}%A9p;gL+nJbsZY)%v>R9?1%?s<}l5YE9|8AHHV?{T}ge&+?OjgVcQ)eAT)Ew zXck_q{RT-{mQ{ER2RS)pU%{zvgsE@2-~@zba*1ZeHC-&Z!bYc9TTw2ozs}~`VdkQ` z**3qs)aL9n%=a!W_|rDYogWAywyywL4i@noq?FnWjQ8s)A0C8T)<{|+<`E@%ng=h^ z++3F4jz?`+yj^hnXiZ)KzB&R=Y!bY@%he9w8=-l2vQ2)#H;}eimNg*;kaINiF5ruu zXzGMZmPu69fHNdtvvh_{`M?ub@KPKL+QXl^ApLTj&HWR76Sr328q2b3x}Q*iIhx6s zx@?%W(go+NJk2Xto;WxV4-P<3!d>N>yUShKdggP{vHG_!`gTyxuhB`sm!On!w@Do0|DgDdNKC` zuo1(f#j>#j4thSJ2xx8oKGbRSkk-%|Hs4j$+?yAGM=y2<{MVO)#tvTqDhG;!m_hvp(dm1IDUtAuYX)!bFCnYlue z8-O>jw#kvO;1j`TErOd$HS@~N27GG>j2o!=yhTtS5>z(|W{>nmVUG=Bsw^_5^#_PK zby8?`s05h~(4zVVQCG?p_CbkHF?8 zC0FkS2WY{kS7*{6*STR)>eUv5);`BXpF=ZFJ&C5H?+Gk(y)T$*M_@b%3uRh zbJ&PWoWefAtq({T;dyR-n=c$RRAMT-GK=n_#_9kJ%z-n4sjn}}HOd;G#ZxC{1F!57 z{PhdL?oj&kIM?Iq?pD44G&qP4NqO(C56DUFacUqx0D00)r*_{Su?6>UFdVgbs?9kC z>9Nzm939hAZ4m*PYog+yUv6hCPSVGpR@63Pd2|>dFpgzFm=Gmh>hS2SJf8yBAm&iN zB$Gh`(ku#~C2T-SMgpTqk90X(lb^I&|6KsMJSCWpfiwoRx4FfjL6aJqBV-^kYt%Jl`H28pRd&X}eScDMVv zy5ws5ink@NILWSCM`?=lQja^$8Ta~?k~=?;>}WH*bor2Y5qf{Wh#8BD5?ACir0o)O z9+WZtWVAcs>H~s(?e1@0U9wvV2IXoVzR2dj@j9m10Xl8L(Q4;`i8h0BHBDi`fA)80 z32Z7nTC=kY-u%+dB1|2k{RlGvb_%7`OklYmqs{w3@Wvj)9X~kN=J_dR;rXp(SIZN{ElqNy6^8~50lMwy88pcV3K^hYLXZZ5N9R^@>Wf(JLmgS4sX#K(WR zUht#$B?mjrn8OM*&s6w&dKl`Ks;nIxh9dCOYRT$-E->exsrkoro8gx6Yerg{=ce?0 z*W06T??r|RF~GJ~!6nNAJXR~%(FU*X zmfXEg9QUYZe>*&1C%9ypo44 znjM$V@zT=Wg1gqmJ4d&Uvbq04osEjK#Kj8^&G-9h0cpJK^)qe0c~)X^s%;WnyFw<_ zOO@wntf0Afc!6f+cf0t*Wjb5sc=M0WK6p8(2>6&Rmfx*vfG0LfK0O@IO(QK$DBc4+MHYL%?x_*HR4=iDn%N^YSDg))ouwIC zz^Nn~xYx3*r;~D!15j3rI91ee!(}BfYq&n9FyRzZ!XnOIQ3iaHC}A0JLNRhKT=;(k z&cbA^n@JhmDbIh0zF%#>2B=KdvYAo?IE^0f(Rcebnt&OpS~dIIp)zTQa`aI(FsBcK zGp8s0t3C2{oX)&?eF&U+J?S>=k%%27ZhfIOb52jeNgE)KPU>$9KaNhC;h4wXWXflx z3(g4vlD8fL d)hk~U_&=J>0=>+}4n+U}002ovPDHLkV1iATCb9qk literal 0 HcmV?d00001 diff --git a/lib_theme/src/main/res/mipmap-hdpi/ic_shortcut_uart.png b/lib_theme/src/main/res/mipmap-hdpi/ic_shortcut_uart.png new file mode 100644 index 0000000000000000000000000000000000000000..e1e7bb3b3e20bdbe880056b4c7da97c56e47ae5e GIT binary patch literal 2196 zcmV;F2y6F=P)C62l zH%-qy_uSw4-sgGF^E|)j`3Z&zN~tU$AD9Hp2NnUdfij>Nm+M+i3355)@lFnabX>$skW#7^SeW4l zfYVaSMST+FZq1ZtcIT z-Iyd}K*S9EnI2>34KydLlfM@bkR5;tfwXO-0>}?y@0F)~JNQ}{_Pr~3@x0)C2SkiO zUP5~#kp`n!HEwA&z}?Qs`hxN zP4lBA#?SWV2eDNaDNE16OM)!VaA<>OPCmT+p5VK03EIN;mo-VG#SE}vaf><0VkJ)q zW=Pn#QuE++OV*ET;Lv5kxBhVEQbVURPxF-amY&&+{y$Z9TJY>nf-Zu{O3t)z{b5)Pv zzionzN31@5uGAo$q-tLR#B2{+pOX?M=h(kJj;O8(Tnk$n$ngx%EYtjHjpo<)MlI{~ ziEwbe=Ez3PrjqDdU!7xkWu4~L_3>8t)%BVK>okv*MR&To2o7)1ytYwOm~CPt+NyL{ zkCnj5IdInmUj&tT*y5#>v9ZK(w%tlYZU73h>;>x>bxAx~dhL=)usdMaNH#ZC5;`OH zZ?*?FV)&etZ4ZaqR(lLAn_>v~b6$#r*vhVyv16|0@Z|t??XaW}7EFRSnjowNyWaHJ zf6?&ghUir`bP66m=8@yUsjE@z^xJm@7nR`eSK`-Sci5vm1jnyL=T?6t`0Npn<{O}T zqT?HiEsL);+W%J-`O`08s)x^{{Ja7PWgC80>mh?sF(rCY9X%qBoW4M(Rm72FrActK zQE)@YkGFPZ;&96+gahrlCCyXWOl-v0|!UehB&Yo(rC;Yn^ zzBxaB19we;?=3WZ@e_t`Er`Ciw%AZvX4t*hP?jGZuUevcWTxTg%cI22kcOwq4PPrW ztWP+1Kbdd1zsT_RRj6whY@J~Urly+^&Cu8h z|A;ZTb!~zM1!r2Jz9YKU(MCb@4LIytSYU+rw6{`$oWnCl(mavr0j0Qm4aym(%4>;pktw+$Tvw<%Yt4YY@C%=J*I;nCS9o!?altPn2c z`aJNJbY>K_8GOnjEh?Unpi2XO4?Gr>QihdM)xek2m6sO6UhpV}#x}}?dkLw=Hil&2 zJQK%>l^EAj)R5sNdWiP6S;eZ4&SaJIb81rgaOUAAx0|4t05|h^oIwzkdBD;T$jUsTYmh``9`ZIwqDJLo0{;)< WRWetKjEsc<00003GWer(f-MZddTcgI!nl!t~y8bX(vnIxv=>Bn6qelN2d_vs1HuGgLY!O98eqIg6aNSohd}VBw>$>o`-HNj_Fc8ZkR5Q=`0Ts!R5kQgi>64RO_Twf-c!$e9AM2cnegsvo#kol}mz?CdX zMZXX!<^ee<*)l7VYn&pB)|{xw(=4K z5r`=t6(4-PcL~-N349jukVvvPHYI^~5A9nYfz7%CZwEhii@-)>XId=+GB06QMjiZ8 zYyQ6oFyOycbf9ct9G0O;oN2M+KyD*^Q@uaz|ad$Jpl(>nsE(}}^W zb9kew6Wa_m-XXy1y=Un{_v8#F=jIXGYR6rfb!(FNRaQN$jwwlEZeam^)3bQJqTM?y zz^Vs*TaSd!A>h+)OtE)zU^`hLtH+@LX}^r^Co94@Xyl(`Wf=9c4hK`AbcrItSn6YP?xx!x(`t=(v?*rl7W$VNNerI`A$0mV~h}C`4Evr zBEH z7qJkBbl84GkB^NOT^gN;1;12%ar(SMAT(E1|AO+&=psIeIb}ZR ziap#5v06vS^M~Vb|LF`g*@rdoyc%g{EAEm6GBof1T>ZR7^_hUqjX+f4O=#vwv4E2q z$ojEMRuvb4_V#u(H8&&0+J>O;95?W*s2^!m1mTTMSLzh{ikm~=cG@GtntNrB<5bJ5 zp`i^`4UKR(9GIh($m!_AeY$*Eoiz0syu0^Z>NA0uGN*{IWI~=Lz%4~^LocS)os|Z+wAzQ-neKTt@5~epTWnLP?ReKbSBRc*h2mO+h!X^XO$Al z_9^KeKu~D5R_~w7EJLefLR*>Q%R_jrpboDat#~S<0>08ZYURX_ zzP?=92e>)Zm3W50GX>4CO)QqnPO{1$QcY^9bXA)y&GS#CmY{`JqG@;>PiIsjP+x>Y zmUawG&mbtdd=WgWA5Y1vzeohA9nqEDcmjK}t!NybTGAH3lwGL+XVqQRhHE`1ELEJX zl&}}bUdQV@m82!>+?`M=6aSAnb*gnr74;M7S@Bg)kz~H{8`&M{b+|Yk zI$EAMyG(VrQb`HxLkSzx+yhhZFz$>kf)6F_ONCZ>K6oI#dJ#OUpGe+MF!Qf-H=e+w zIX`H=XZ1cQZ-gHMmWr#uRn?bN8!DGiq*tPSYzi$SlgRHGl4pZ|Tf5<>FIxo9iOcPH zqS^Ag;JL~!tJZoSsx}WADiB=Lfsm%lc%`TbTN%8|_iPp&xgT#5}U$W{!a{*oakt zyHuB!SqJ##1l~Wie{}+`mj?g*>K;mk^rV*@#_0~ls8Gie%E@%belMlWsqv{KqkVU*AqN8 zrbLO@QYB=PlG@yuaf#$A!}lerim&t>*W7&X(S%IGwqrf-$hL^RkMEeW~^ eVQ*cVebpleSd#{e1DwFbDnd~^E>DHJ?A-jC~qVP2n7NF0FZ~f%b9H( z{aX~}w{>BJ-v|HzJnG?c{A}X1utMqN*(sd?Ki5jwK^RRDmZk6C@bt1@M2&xSQ|^n@ zrCRIvlzTdQy6U8HuEiizx3vOe|~;iTAElfhM2!)-k!U$u_4LmQYoY1V<|5iFzjR-m03YP zx5S26;eot>Yb4htm% zcO&&js7Dz0lQ(d@J=i79d*JM1-~!W~5YXYfva+(vy7VM+E~ea=brU7^Ks2L+ zO25Di^7AVR^!44iOd@?xefrn5p8KuP)yZcUN-L{ttk%bnC=9CH7U>SRfx}NkG)^Ti z4wrt^K+BH%Ro^YtA5DLWX{@PPP$KuJB6^2AlGf3>b_u4?z;pS~rpn4n5m^Du7&o+@ zH%+eG|Fm3XsNsjd7e4-=FROt)YQHn^{PCu z+zC0KF$#akDos$t!)b5 zRiVOSopqxA^!Ztl*f9Im#!So8l}P*+w8n$Uu`-UOY}Q>Li~jhf`q<&vSrNPgt4BFJ zel-L^W}oxHVzD4F+HPsC?>cWA^h1FF_xSN+4}n0SpN|c4hSd7(6{v@uLwzdmqR1S` zIVm@s$YtT*nm2&u4|Suts?M7)7dRZw8y0q=p`-h~Bl)e?=`akc5+zSxenDPbUTzZ& z-rkq@mJ#wZ6@!_5`1x+JYEk|`4z5Y!T#mjh4Q zTAS4CRZ@sHaUB7p!d%x?p?+8 z^>xR&{`oI@NJT%M!LM?cTLc0;OONNW^YJG!qV~YRwTi}I2mFYrkX>8TmEzG z%h!e13g=D<$i#F~MG-Ac}G$<2ahg&!>K1poAV^kL={%-FaHsHF5q48QYOurrtS+1%n* zd!mp_ZE=wL zs%kU_;E=E1PbXJ4cxw= zdS+&3RZ5rK5-wt~>2JMZ<3~w(Gd9kCCOaqKSq^U9nzA>hTU%Q}9X*MgU#1T1f3gEcEea;C#x;n+M3lhiGofue`mz zrQ+k`yL?j6*X+rPe+`I8en{kiZR;gg4tlV>{oAtn7RsC2t$Vkipy0a;D-zmdh;T3L zQfu}db?_sJMgPWEsQ+ub#JJ50t`?izYp#2K>HSuQ%#Fhu;QsGC9xt`7{384Vy^B&( z(gikIe`E<4-A`&_KsMb@-8}AWOInuKN=jJET6!0XMkf?V)exy_6KCL^eNMj|qR7L- z67Xb^)K^?=?5W$KOa?>T10la6vYUE+^!-)!dAho~u8et5f8V^FGB=!9WS$TouWJ=+ zXk=tAN4Hp3M}KB%KIuE5(SyU|+vkKr-C?Qzm`rN2u4$^eB-n1l+ zsFMzOKNt!*O4D4XtLEiNv~3`=Idt4C+=%cSm@ zk57qs4I)jg#Qc=dT(g}JMgkQGEzvy+oQ^7FMV7$Y2x72>?Tp#%daOdYzzX;KZE za}V1wY~iQ_ACJIdy9S)pGS9?hcI$Cn9tu%be>On?Ll{!fOM?|{%%r}dK zk>QAS2Ni>QS|XO*$J-0|S5K$N*11h*u|e@#IFd@G#&)NPWR9?P|FGR|bXp0c!; zC0q6K=mER;N6Cec`408XJnQE1W7I)$KA8{YC~=U8kbb+QKa@;~B|vLK12dWnW4_tR zjK$K#Q4Yws@9+R^udPEXmt`b1o#+%5l{qQD-`_l8Z{Bp(-QUsUqsj(`#^n2(Sf-qQ zoyWCW;+?_MxZgb%Dh!dNYc}4tH+NFlTDf!Q)fNV~H=`!>os^a+a2F#1hSN_eW~%4B z8X|E2^%)3%iJYzRrg4f89Ae{2Ym1c4W|Zld^+@&O)0bfX`$5A2m-*msULjq|t9|>K O1bDc5yD$)8S^oh#8v#oI literal 0 HcmV?d00001 diff --git a/lib_theme/src/main/res/mipmap-mdpi/ic_launcher_round.png b/lib_theme/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..3bda01c2222f33e06e68e0f0e42b1ec205589680 GIT binary patch literal 3182 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 Xh^Iz;ZWfRDZ$e#7Lyammhp7JnF>eoj literal 0 HcmV?d00001 diff --git a/lib_theme/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/lib_theme/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..bb45268892ce77d1c8f06205280fc97dd49ae228 GIT binary patch literal 7362 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 literal 0 HcmV?d00001 diff --git a/lib_theme/src/main/res/mipmap-xxhdpi/ic_launcher.png b/lib_theme/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..3fc95be39e531fd87c25e24df1cfce8a3f1a2b22 GIT binary patch literal 6974 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 N+MA$aLyBZ@o#H;zzD>E@F1Rpb zXk;q1t?cG!D2lV~pt7?TUnHjFDwbbvojb8l5_GEc?zkLy>MD!B9fy|=3I0|gXjU-5 zX?r;{Uor6l@9Un$?o+VYqvrKD$7!ek90%%zliK`SV4hB++kFb=Y%n;}+FDy3{ZyWt)Nv|17wf|>d5y3kuE1uOUUAMv9UJh9hs?XNK=OL6Tm z?FbZhICCy_`!%3Ow^80ws9f`-0G7XNaHOt(5NLI&aP2ZAP<*yPF*sGbA=fyZu?Zxl z+>IRx2B(|+O`aVm;QqJWrD;wI+4*BZe{yx>}I~o!}ahVQ?+fta*iu)3G}3{6TN7heFWex{taF{FNEMX3-Z1!mnYQf#qHwYizOzHP zn(SaVty@;2Z4JnB+O&Z`MN;IVeS48&@i>cUPqY2{-W`JH59lw6DjVRdTa1{^ zd++&#qCnq1+3VUi;5EGVHXZT6C5lNGxIdYVmf&04jSefCD%E%G15P!;*WWbQdcu2* z-yCak>qS~upl!#INCIs`qX08E{O6Sx<2(-)72n!!uw<*?xJUE0!*%v?NK-F+yF zpW6_-J?GcPSp4u(rDK1Lv#4@7;z^)N;6eC>6Ey{Em&Gk1i=4?bz%lg39hFA|Z2(T+gQ-nJId+Pg9; z3wF%*B9VY2c@<@eo{hG+ui14r-?ez`~RZ-4LJN^JjZ*t0HAM`*U}qm@_|R$FYjKJ2$nL&K$% zQO&1ZXWaSV(|)?UrQ;PVudtXfpzGVgq`xHb^oOEL`De+7-RnZcDE*R_q`0Nf;-k5~ zBhS-r5$&si&u=nZ^V+B8 z+D(BkAA}#fE9OZlT~pv=$^2?yrSI-E&6Y+xXIm6yDaTUlyhyQ>;HeJ{Zr&JwyUB|r zjL&I(og@?kQWW=1u=vw87E^P5NuHI&$X<>)MW$(93%rCz)jQ<pSW?ItFi43d(BZ zW5oPX7XNx#eCGA4{elPI5nP!I(}yZPJxu9(qTU&pz>QMMSGq(XheU4zQ}8jG!gjID zDQlx+tlcmx-_D{a&W2&>#APL!El^rxXOU(m<9BS8QcjKafzCWz3utrt>PMs`0nNCQ z#}JA%!%jIa834bYYISSjWe6@AM1R48jy}*KfuxjsOw+svSmEcHa68txDo=4`p51!; zvO%pWKLG%^M@qS;+XQm(3~)Cv#;>!n(==BB%R8{#v_(k3iisAt7pZ_A9T=j(Dk){BTsUz(d!}iAgof=gJ3_qvobw!I zQp!=C{cya}WV#<`Bp^@Et#Mogl2RT5mIia&Z?V)_P{bH9aD(6DWf_d*ZSk@KtbeTX7iJl8c)Njc1Tvxx` zU7yb5!1KWx_!iGQYuI%Qx4R=SO>-#lIzIFdo#!Y6u9Q+9>r(W?i!Trc zvZa*WE|8OuV+m&I4g+**<#n|Dn zfpyk*lBXn*I!~`ipa{h}+wnOB=v|aKrux2~yhx8n7dqzRl>{E^-o*3RPQo59gxqVP zR;Fnt1B-x%g7xiDECU{wQa1O-#d#6Lv*3Bj7`)U}0Abn;vfr zbH5`{8zOzrkw{|#e#FFnN89Zu3$ z@kn9pfUbInFi!%z{Y;pCO&}LSl>pO#0$??AOjV zsO^t9Zz|`QrkUy}-5EfRL!d0|Bb14b9yJg0 l1Ue1W0ZmfM#`9rb{~x^Dv!XTzuHXOw002ovPDHLkV1iAgFqZ%T literal 0 HcmV?d00001 diff --git a/lib_theme/src/main/res/mipmap-xxhdpi/ic_shortcut_uart.png b/lib_theme/src/main/res/mipmap-xxhdpi/ic_shortcut_uart.png new file mode 100644 index 0000000000000000000000000000000000000000..9fe1e193565e717f906823d0c09941ceb53ea38b GIT binary patch literal 4876 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 literal 0 HcmV?d00001 diff --git a/lib_theme/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/lib_theme/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..73d15a60e966d657f27641ec97740f0519a7035b GIT binary patch literal 16696 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 E05G@sPyhe` literal 0 HcmV?d00001 diff --git a/lib_theme/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/lib_theme/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..93fc534ddcdb0bafa2c7dcfeceecc02cd6b2bf02 GIT binary patch literal 17708 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 literal 0 HcmV?d00001 diff --git a/lib_theme/src/main/res/values-night/colors.xml b/lib_theme/src/main/res/values-night/colors.xml new file mode 100644 index 00000000..c2f2aef6 --- /dev/null +++ b/lib_theme/src/main/res/values-night/colors.xml @@ -0,0 +1,11 @@ + + + #FF212121 + @android:color/black + #FF008CD2 + #FF008CD2 + #FFFFFFFF + #FFFFFFFF + @android:color/black + #FF0090B0 + \ No newline at end of file diff --git a/lib_theme/src/main/res/values/colors.xml b/lib_theme/src/main/res/values/colors.xml index 009a9678..cdc236f2 100644 --- a/lib_theme/src/main/res/values/colors.xml +++ b/lib_theme/src/main/res/values/colors.xml @@ -6,4 +6,6 @@ #FF0077c8 #FF004c97 #FFFFFFFF + #FFDADADA + #FF0090B0 diff --git a/lib_theme/src/main/res/values/strings.xml b/lib_theme/src/main/res/values/strings.xml new file mode 100644 index 00000000..522d4b3d --- /dev/null +++ b/lib_theme/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + nRF Toolbox + + Disconnect + Battery + \ No newline at end of file diff --git a/lib_theme/src/main/res/values/themes.xml b/lib_theme/src/main/res/values/themes.xml index c9f88cf9..4d31cda5 100644 --- a/lib_theme/src/main/res/values/themes.xml +++ b/lib_theme/src/main/res/values/themes.xml @@ -2,6 +2,7 @@