diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ef135bbec..ac6d69df4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,7 +33,7 @@ plugins { alias(libs.plugins.nordic.hilt) } -if (getGradle().getStartParameter().getTaskRequests().toString().contains("Release")) { +if (gradle.startParameter.taskRequests.toString().contains("Release")) { apply(plugin = "com.google.gms.google-services") apply(plugin = "com.google.firebase.crashlytics") } @@ -53,7 +53,6 @@ dependencies { implementation(project(":profile_hts")) implementation(project(":profile_prx")) implementation(project(":profile_rscs")) - implementation(project(":profile_uart")) implementation(project(":lib_analytics")) @@ -62,16 +61,19 @@ dependencies { implementation(project(":lib_service")) implementation(project(":lib_scanner")) - implementation(libs.nordic.core) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) implementation(libs.nordic.navigation) - implementation(libs.nordic.blek.uiscanner) - implementation(libs.nordic.uilogger) + implementation(libs.nordic.logger) implementation(libs.nordic.permissions.ble) implementation(libs.nordic.analytics) - + + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.blek.client) + // Pass SLF4J logs to Timber + implementation(libs.slf4j.timber) + implementation(libs.androidx.core.ktx) implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 6779d7fcd..e69de29bb 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,20 +0,0 @@ - -# Simple XML --dontwarn javax.xml.** - --keep public class org.simpleframework.**{ *; } --keep class org.simpleframework.xml.**{ *; } --keep class org.simpleframework.xml.core.**{ *; } --keep class org.simpleframework.xml.util.**{ *; } - --keepattributes Signature --keepattributes *Annotation* - -# Ignore our XML Serialization classes --keep public class your.annotated.pojo.models.*{ - public protected private *; -} - -# Crashlytics --keepattributes SourceFile,LineNumberTable # Keep file names and line numbers. --keep public class * extends java.lang.Exception # Optional: Keep custom exceptions. \ No newline at end of file diff --git a/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt b/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt index 835d019f2..c14fae1ff 100644 --- a/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt +++ b/app/src/debug/java/no/nordicsemi/android/nrftoolbox/NrfToolboxApplication.kt @@ -37,6 +37,7 @@ import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.AppOpenEvent import no.nordicsemi.android.gls.GLSServer import no.nordicsemi.android.uart.UartServer +import timber.log.Timber import javax.inject.Inject @HiltAndroidApp @@ -54,6 +55,8 @@ class NrfToolboxApplication : Application() { override fun onCreate() { super.onCreate() + Timber.plant(Timber.DebugTree()) + analytics.logEvent(AppOpenEvent) uartServer.start(this) diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppModule.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppModule.kt deleted file mode 100644 index 0e242fad4..000000000 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/AppModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package no.nordicsemi.android.nrftoolbox - -import android.content.Context -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import no.nordicsemi.android.common.logger.BleLogger -import no.nordicsemi.android.common.logger.DefaultConsoleLogger - -@Suppress("unused") -@Module -@InstallIn(SingletonComponent::class) -internal class AppModule { - - @Provides - fun provideNordicLogger( - @ApplicationContext context: Context - ): BleLogger = DefaultConsoleLogger(context) -} diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/TitleAppBar.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/TitleAppBar.kt index 50057e80f..47e572695 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/TitleAppBar.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/view/TitleAppBar.kt @@ -32,27 +32,16 @@ package no.nordicsemi.android.nrftoolbox.view import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.ui.res.colorResource import no.nordicsemi.android.common.analytics.view.AnalyticsPermissionButton -import no.nordicsemi.android.nrftoolbox.R +import no.nordicsemi.android.common.ui.view.NordicAppBar @OptIn(ExperimentalMaterial3Api::class) @Composable fun TitleAppBar(text: String) { - SmallTopAppBar( + NordicAppBar( title = { Text(text, maxLines = 2) }, - colors = TopAppBarDefaults.smallTopAppBarColors( - scrolledContainerColor = MaterialTheme.colorScheme.primary, - containerColor = colorResource(id = R.color.appBarColor), - titleContentColor = MaterialTheme.colorScheme.onPrimary, - actionIconContentColor = MaterialTheme.colorScheme.onPrimary, - navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, - ), actions = { AnalyticsPermissionButton() } diff --git a/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt index 7928ab0fb..06f00f7ce 100644 --- a/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/no/nordicsemi/android/nrftoolbox/viewmodel/HomeViewModel.kt @@ -58,10 +58,9 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( - @ApplicationContext - private val context: Context, + @ApplicationContext private val context: Context, private val navigationManager: Navigator, - private val activitySignals: ActivitySignals, + activitySignals: ActivitySignals, cgmRepository: CGMRepository, cscRepository: CSCRepository, hrsRepository: HRSRepository, @@ -114,7 +113,7 @@ class HomeViewModel @Inject constructor( } fun openLogger() { - LoggerLauncher.launch(context) + LoggerLauncher.launch(context, null) } fun logEvent(event: ProfileOpenEvent) { diff --git a/build.gradle.kts b/build.gradle.kts index b8e2230ab..228398beb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,9 +34,10 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.kapt) apply false - alias(libs.plugins.ksp) apply false alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.kotlin.parcelize) apply false + alias(libs.plugins.compose.compiler) apply false + alias(libs.plugins.ksp) apply false alias(libs.plugins.hilt) apply false alias(libs.plugins.secrets) apply false alias(libs.plugins.protobuf) apply false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 876a631cb..ae6b5bf43 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,37 +1,6 @@ -# -# Copyright (c) 2022, Nordic Semiconductor -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without modification, are -# permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, this list of -# conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, this list -# of conditions and the following disclaimer in the documentation and/or other materials -# provided with the distribution. -# -# 3. Neither the name of the copyright holder nor the names of its contributors may be -# used to endorse or promote products derived from this software without specific prior -# written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -#Mon Feb 14 14:46:55 CET 2022 +#Thu Aug 29 13:13:59 CEST 2024 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/lib_service/build.gradle.kts b/lib_service/build.gradle.kts index 566cef72c..798b99d1c 100644 --- a/lib_service/build.gradle.kts +++ b/lib_service/build.gradle.kts @@ -40,7 +40,7 @@ android { dependencies { implementation(project(":lib_ui")) - + implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.blek.core) diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt index c9ae50fc0..7e394cec0 100644 --- a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt +++ b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManager.kt @@ -37,5 +37,5 @@ const val DEVICE_DATA = "device-data" interface ServiceManager { - fun startService(service: Class, device: ServerDevice) -} + fun startService(service: Class, device: ServerDevice, profile: String) +} \ No newline at end of file diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerHiltModule.kt b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerHiltModule.kt index b9b0eba90..9a23dc0e7 100644 --- a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerHiltModule.kt +++ b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerHiltModule.kt @@ -13,8 +13,7 @@ class ServiceManagerHiltModule { @Provides fun createServiceManager( - @ApplicationContext - context: Context, + @ApplicationContext context: Context, ): ServiceManager { return ServiceManagerImpl(context) } diff --git a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerImpl.kt b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerImpl.kt index 1872a84df..5bdb6614b 100644 --- a/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerImpl.kt +++ b/lib_service/src/main/java/no/nordicsemi/android/service/ServiceManagerImpl.kt @@ -7,11 +7,10 @@ import no.nordicsemi.android.kotlin.ble.core.ServerDevice import javax.inject.Inject class ServiceManagerImpl @Inject constructor( - @ApplicationContext - private val context: Context + @ApplicationContext private val context: Context ): ServiceManager { - override fun startService(service: Class, device: ServerDevice) { + override fun startService(service: Class, device: ServerDevice, profile: String) { val intent = Intent(context, service).apply { putExtra(DEVICE_DATA, device) } diff --git a/lib_ui/build.gradle.kts b/lib_ui/build.gradle.kts index 41ca294fe..4ec95543e 100644 --- a/lib_ui/build.gradle.kts +++ b/lib_ui/build.gradle.kts @@ -42,7 +42,7 @@ android { } dependencies { - implementation(libs.nordic.uilogger) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) implementation(libs.nordic.logger) diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt deleted file mode 100644 index 3e668674c..000000000 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactory.kt +++ /dev/null @@ -1,9 +0,0 @@ -package no.nordicsemi.android.ui.view - -import android.content.Context -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher - -interface NordicLoggerFactory { - - fun createNordicLogger(context: Context, profile: String?, key: String, name: String?): BleLoggerAndLauncher -} diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt deleted file mode 100644 index ce8bc4a7f..000000000 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/NordicLoggerFactoryHiltModule.kt +++ /dev/null @@ -1,28 +0,0 @@ -package no.nordicsemi.android.ui.view - -import android.content.Context -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.common.logger.DefaultBleLogger - -@Module -@InstallIn(SingletonComponent::class) -class NordicLoggerFactoryHiltModule { - - @Provides - fun createLogger(): NordicLoggerFactory { - return object : NordicLoggerFactory { - override fun createNordicLogger( - context: Context, - profile: String?, - key: String, - name: String?, - ): BleLoggerAndLauncher { - return DefaultBleLogger.create(context, profile, key, name) - } - } - } -} diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/StringConst.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/StringConst.kt deleted file mode 100644 index 48393b27f..000000000 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/StringConst.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2022, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package no.nordicsemi.android.ui.view - -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext -import no.nordicsemi.android.ui.R -import javax.inject.Inject - -class StringConst @Inject constructor( - @ApplicationContext - private val context: Context -) { - - val APP_NAME = context.getString(R.string.app_name) -} diff --git a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/TopAppBar.kt b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/TopAppBar.kt index 95b3974c9..20e7e5f36 100644 --- a/lib_ui/src/main/java/no/nordicsemi/android/ui/view/TopAppBar.kt +++ b/lib_ui/src/main/java/no/nordicsemi/android/ui/view/TopAppBar.kt @@ -32,18 +32,15 @@ package no.nordicsemi.android.ui.view import androidx.annotation.StringRes -import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Close -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp +import no.nordicsemi.android.common.logger.view.LoggerAppBarIcon +import no.nordicsemi.android.common.ui.view.NordicAppBar import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.ui.R @@ -51,123 +48,44 @@ import no.nordicsemi.android.ui.R @OptIn(ExperimentalMaterial3Api::class) @Composable fun CloseIconAppBar(text: String, onClick: () -> Unit) { - SmallTopAppBar( + NordicAppBar( title = { Text(text, maxLines = 2) }, - colors = TopAppBarDefaults.smallTopAppBarColors( - scrolledContainerColor = MaterialTheme.colorScheme.primary, - containerColor = colorResource(id = R.color.appBarColor), - titleContentColor = MaterialTheme.colorScheme.onPrimary, - actionIconContentColor = MaterialTheme.colorScheme.onPrimary, - navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, - ), - navigationIcon = { - IconButton(onClick = { onClick() }) { - Icon( - Icons.Default.Close, - contentDescription = stringResource(id = R.string.close_app), - ) - } - } - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun LoggerBackIconAppBar(text: String, onClick: () -> Unit) { - SmallTopAppBar( - title = { Text(text, maxLines = 2) }, - colors = TopAppBarDefaults.smallTopAppBarColors( - scrolledContainerColor = MaterialTheme.colorScheme.primary, - containerColor = colorResource(id = R.color.appBarColor), - titleContentColor = MaterialTheme.colorScheme.onPrimary, - actionIconContentColor = MaterialTheme.colorScheme.onPrimary, - navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, - ), - navigationIcon = { - IconButton(onClick = { onClick() }) { - Icon( - Icons.Default.ArrowBack, - tint = MaterialTheme.colorScheme.onPrimary, - contentDescription = stringResource(id = R.string.back_screen), - ) - } - }, - actions = { - IconButton(onClick = { onClick() }) { - Icon( - painterResource(id = R.drawable.ic_logger), - contentDescription = stringResource(id = R.string.open_logger), - tint = MaterialTheme.colorScheme.onPrimary, - modifier = Modifier.size(24.dp) - ) - } - } + backButtonIcon = Icons.Default.Close, + onNavigationButtonClick = onClick, ) } @OptIn(ExperimentalMaterial3Api::class) @Composable fun BackIconAppBar(text: String, onClick: () -> Unit) { - SmallTopAppBar( + NordicAppBar( title = { Text(text, maxLines = 2) }, - colors = TopAppBarDefaults.smallTopAppBarColors( - scrolledContainerColor = MaterialTheme.colorScheme.primary, - containerColor = colorResource(id = R.color.appBarColor), - titleContentColor = MaterialTheme.colorScheme.onPrimary, - actionIconContentColor = MaterialTheme.colorScheme.onPrimary, - navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, - ), - navigationIcon = { - IconButton(onClick = { onClick() }) { - Icon( - Icons.Default.ArrowBack, - tint = MaterialTheme.colorScheme.onPrimary, - contentDescription = stringResource(id = R.string.back_screen), - ) - } - }, + onNavigationButtonClick = onClick, ) } @OptIn(ExperimentalMaterial3Api::class) @Composable -fun LoggerIconAppBar(text: String, onClick: () -> Unit, onDisconnectClick: () -> Unit, onLoggerClick: () -> Unit) { - SmallTopAppBar( +fun LoggerIconAppBar( + text: String, + onClick: () -> Unit, + isConnected: Boolean, + onDisconnectClick: () -> Unit, + onLoggerClick: () -> Unit +) { + NordicAppBar( title = { Text(text, maxLines = 2) }, - colors = TopAppBarDefaults.smallTopAppBarColors( - scrolledContainerColor = MaterialTheme.colorScheme.primary, - containerColor = colorResource(id = R.color.appBarColor), - titleContentColor = MaterialTheme.colorScheme.onPrimary, - actionIconContentColor = MaterialTheme.colorScheme.onPrimary, - navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, - ), - navigationIcon = { - IconButton(onClick = { onClick() }) { - Icon( - Icons.Default.ArrowBack, - tint = MaterialTheme.colorScheme.onPrimary, - contentDescription = stringResource(id = R.string.back_screen), - ) - } - }, + onNavigationButtonClick = onClick, actions = { TextButton( - onClick = { onDisconnectClick() }, - colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent, - contentColor = MaterialTheme.colorScheme.onPrimary - ) + onClick = onDisconnectClick, + enabled = isConnected, ) { Text(stringResource(id = R.string.disconnect)) } - IconButton(onClick = { onLoggerClick() }) { - Icon( - painterResource(id = R.drawable.ic_logger), - contentDescription = stringResource(id = R.string.open_logger), - tint = MaterialTheme.colorScheme.onPrimary, - modifier = Modifier.size(24.dp) - ) - } + LoggerAppBarIcon( + onClick = onLoggerClick + ) } ) } @@ -176,18 +94,14 @@ fun LoggerIconAppBar(text: String, onClick: () -> Unit, onDisconnectClick: () -> fun ProfileAppBar( deviceName: String?, connectionState: GattConnectionStateWithStatus?, - @StringRes - title: Int, + @StringRes title: Int, navigateUp: () -> Unit, disconnect: () -> Unit, openLogger: () -> Unit ) { if (deviceName?.isNotBlank() == true) { - if (connectionState?.state == GattConnectionState.STATE_DISCONNECTING || connectionState?.state == GattConnectionState.STATE_DISCONNECTED) { - LoggerBackIconAppBar(deviceName, openLogger) - } else { - LoggerIconAppBar(deviceName, navigateUp, disconnect, openLogger) - } + val isConnected = connectionState?.state == GattConnectionState.STATE_CONNECTED + LoggerIconAppBar(deviceName, navigateUp, isConnected, disconnect, openLogger) } else { BackIconAppBar(stringResource(id = title), navigateUp) } diff --git a/lib_utils/build.gradle.kts b/lib_utils/build.gradle.kts index 6c2f2906e..fda92e5ba 100644 --- a/lib_utils/build.gradle.kts +++ b/lib_utils/build.gradle.kts @@ -44,4 +44,6 @@ dependencies { implementation(libs.nordic.blek.uiscanner) implementation(libs.kotlinx.coroutines.core) + + api("no.nordicsemi.android:log-timber:2.5.0") } diff --git a/lib_utils/src/main/java/no/nordicsemi/android/utils/Ext.kt b/lib_utils/src/main/java/no/nordicsemi/android/utils/Ext.kt index d5bac3617..1034393b2 100644 --- a/lib_utils/src/main/java/no/nordicsemi/android/utils/Ext.kt +++ b/lib_utils/src/main/java/no/nordicsemi/android/utils/Ext.kt @@ -31,25 +31,17 @@ package no.nordicsemi.android.utils -import android.app.ActivityManager -import android.content.Context -import android.util.Log import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch +import timber.log.Timber val String.Companion.EMPTY get() = "" -fun Context.isServiceRunning(serviceClassName: String): Boolean { - val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - val services = activityManager.getRunningServices(Integer.MAX_VALUE) - return services.find { it.service.className == serviceClassName } != null -} - private val exceptionHandler = CoroutineExceptionHandler { _, t -> - Log.e("COROUTINE-EXCEPTION", "Uncaught exception", t) + Timber.tag("COROUTINE-EXCEPTION").e(t, "Uncaught exception") } fun CoroutineScope.launchWithCatch(block: suspend CoroutineScope.() -> Unit) = diff --git a/lib_utils/src/main/java/no/nordicsemi/android/utils/simpleSharedFlow.kt b/lib_utils/src/main/java/no/nordicsemi/android/utils/simpleSharedFlow.kt new file mode 100644 index 000000000..fe88905c5 --- /dev/null +++ b/lib_utils/src/main/java/no/nordicsemi/android/utils/simpleSharedFlow.kt @@ -0,0 +1,9 @@ +package no.nordicsemi.android.utils + +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow + +fun simpleSharedFlow() = MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST +) \ No newline at end of file diff --git a/profile_bps/build.gradle.kts b/profile_bps/build.gradle.kts index 6932100e4..1c7cd14db 100644 --- a/profile_bps/build.gradle.kts +++ b/profile_bps/build.gradle.kts @@ -49,9 +49,10 @@ dependencies { implementation(libs.nordic.blek.profile) implementation(libs.nordic.blek.uiscanner) - implementation(libs.nordic.navigation) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) - implementation(libs.nordic.uilogger) + implementation(libs.nordic.logger) + implementation(libs.nordic.navigation) implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 60a469f9d..4090bd652 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -35,7 +35,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 516e6b727..b78251b2f 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -32,9 +32,10 @@ package no.nordicsemi.android.bps.viewmodel import android.annotation.SuppressLint +import android.app.Application import android.content.Context import android.os.ParcelUuid -import androidx.lifecycle.ViewModel +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext @@ -53,8 +54,7 @@ import no.nordicsemi.android.bps.view.BPSViewEvent import no.nordicsemi.android.bps.view.BPSViewState import no.nordicsemi.android.bps.view.DisconnectEvent import no.nordicsemi.android.bps.view.OpenLoggerEvent -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.common.logger.DefaultBleLogger +import no.nordicsemi.android.common.logger.LoggerLauncher import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt @@ -67,8 +67,10 @@ import no.nordicsemi.android.kotlin.ble.profile.bps.BloodPressureMeasurementPars import no.nordicsemi.android.kotlin.ble.profile.bps.IntermediateCuffPressureParser import no.nordicsemi.android.kotlin.ble.profile.bps.data.BloodPressureMeasurementData import no.nordicsemi.android.kotlin.ble.profile.bps.data.IntermediateCuffPressureData +import no.nordicsemi.android.log.LogSession +import no.nordicsemi.android.log.timber.nRFLoggerTree import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId -import no.nordicsemi.android.ui.view.StringConst +import timber.log.Timber import java.util.UUID import javax.inject.Inject @@ -82,18 +84,16 @@ private val BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1 @SuppressLint("MissingPermission", "StaticFieldLeak") @HiltViewModel internal class BPSViewModel @Inject constructor( - @ApplicationContext - private val context: Context, + @ApplicationContext context: Context, private val navigationManager: Navigator, private val analytics: AppAnalytics, - private val stringConst: StringConst -) : ViewModel() { - +) : AndroidViewModel(context as Application) { private val _state = MutableStateFlow(BPSViewState()) val state = _state.asStateFlow() private var client: ClientBleGatt? = null - private lateinit var logger: BleLoggerAndLauncher + + private var tree: nRFLoggerTree? = null init { navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(BPS_SERVICE_UUID)) @@ -113,10 +113,16 @@ internal class BPSViewModel @Inject constructor( fun onEvent(event: BPSViewEvent) { when (event) { DisconnectEvent -> onDisconnectEvent() - OpenLoggerEvent -> logger.launch() + OpenLoggerEvent -> openLogger() } } + override fun onCleared() { + super.onCleared() + tree?.let { Timber.uproot(it) } + tree = null + } + private fun onDisconnectEvent() { client?.disconnect() navigationManager.navigateUp() @@ -125,9 +131,11 @@ internal class BPSViewModel @Inject constructor( private fun startGattClient(device: ServerDevice) = viewModelScope.launch { _state.value = _state.value.copy(deviceName = device.name) - logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "BPS", device.address) + tree = nRFLoggerTree(getApplication(), "BPS", device.address, device.name) + .apply { setLoggingTagsEnabled(false) } + .also { Timber.plant(it) } - val client = ClientBleGatt.connect(context, device, viewModelScope, logger = logger) + val client = ClientBleGatt.connect(getApplication(), device, viewModelScope) this@BPSViewModel.client = client client.connectionStateWithStatus @@ -205,4 +213,8 @@ internal class BPSViewModel @Inject constructor( analytics.logEvent(ProfileConnectedEvent(Profile.BPS)) } } + + private fun openLogger() { + LoggerLauncher.launch(getApplication(), tree?.session as? LogSession) + } } diff --git a/profile_cgms/build.gradle.kts b/profile_cgms/build.gradle.kts index 7fb0265c3..242619469 100644 --- a/profile_cgms/build.gradle.kts +++ b/profile_cgms/build.gradle.kts @@ -45,14 +45,14 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation(libs.nordic.core) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) - implementation(libs.nordic.uilogger) + implementation(libs.nordic.logger) implementation(libs.nordic.navigation) - implementation(libs.nordic.blek.uiscanner) implementation(libs.nordic.blek.client) implementation(libs.nordic.blek.profile) + implementation(libs.nordic.blek.uiscanner) implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMRepository.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMRepository.kt index 7ef38ebf4..96d5f15fb 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMRepository.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMRepository.kt @@ -37,30 +37,30 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map +import no.nordicsemi.android.cgms.R import no.nordicsemi.android.cgms.data.CGMRecordWithSequenceNumber import no.nordicsemi.android.cgms.data.CGMServiceCommand import no.nordicsemi.android.cgms.data.CGMServiceData -import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.common.logger.DefaultBleLogger +import no.nordicsemi.android.common.logger.LoggerLauncher import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus +import no.nordicsemi.android.log.LogSession +import no.nordicsemi.android.log.timber.nRFLoggerTree import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst +import no.nordicsemi.android.utils.simpleSharedFlow +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton class CGMRepository @Inject constructor( - @ApplicationContext - private val context: Context, + @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val stringConst: StringConst ) { - private var logger: BleLoggerAndLauncher? = null + private var tree: nRFLoggerTree? = null private val _data = MutableStateFlow(CGMServiceData()) internal val data = _data.asStateFlow() @@ -93,9 +93,12 @@ class CGMRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "CGM", device.address) + tree = nRFLoggerTree(context, "CGMS", device.address, device.name) + .apply { setLoggingTagsEnabled(false) } + .also { Timber.plant(it) } + _data.value = _data.value.copy(deviceName = device.name) - serviceManager.startService(CGMService::class.java, device) + serviceManager.startService(CGMService::class.java, device, context.getString(R.string.cgms_title)) } fun onDataReceived(data: List) { @@ -124,11 +127,7 @@ class CGMRepository @Inject constructor( } fun openLogger() { - logger?.launch() - } - - fun log(priority: Int, message: String) { - logger?.log(priority, message) + LoggerLauncher.launch(context, tree?.session as? LogSession) } fun clear() { @@ -140,7 +139,8 @@ class CGMRepository @Inject constructor( } private fun clean() { - logger = null + tree?.let { Timber.uproot(it) } + tree = null _data.value = CGMServiceData() } } diff --git a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt index e20c50bc8..a425e9593 100644 --- a/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt +++ b/profile_cgms/src/main/java/no/nordicsemi/android/cgms/repository/CGMService.kt @@ -71,7 +71,8 @@ import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService import no.nordicsemi.android.utils.launchWithCatch import no.nordicsemi.android.utils.tryOrLog -import java.util.* +import timber.log.Timber +import java.util.UUID import javax.inject.Inject val CGMS_SERVICE_UUID: UUID = UUID.fromString("0000181F-0000-1000-8000-00805f9b34fb") @@ -132,7 +133,7 @@ internal class CGMService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val client = ClientBleGatt.connect(this@CGMService, device, lifecycleScope, logger = { p, s -> repository.log(p, s) }) + val client = ClientBleGatt.connect(this@CGMService, device, lifecycleScope) this@CGMService.client = client client.connectionStateWithStatus @@ -149,6 +150,7 @@ internal class CGMService : NotificationService() { val services = client.discoverServices() configureGatt(services) } catch (e: Exception) { + Timber.e(e) repository.onMissingServices() } } diff --git a/profile_csc/build.gradle.kts b/profile_csc/build.gradle.kts index 63c49608a..8ef27310f 100644 --- a/profile_csc/build.gradle.kts +++ b/profile_csc/build.gradle.kts @@ -45,9 +45,9 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation(libs.nordic.core) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) - implementation(libs.nordic.uilogger) + implementation(libs.nordic.logger) implementation(libs.nordic.navigation) implementation(libs.nordic.blek.client) diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt index 2964f6645..b0f3c5dd1 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCRepository.kt @@ -37,9 +37,8 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map -import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.common.logger.DefaultBleLogger +import no.nordicsemi.android.common.logger.LoggerLauncher +import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice @@ -48,20 +47,21 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSize import no.nordicsemi.android.kotlin.ble.profile.csc.data.WheelSizes +import no.nordicsemi.android.log.LogSession +import no.nordicsemi.android.log.timber.nRFLoggerTree import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst +import no.nordicsemi.android.utils.simpleSharedFlow +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton class CSCRepository @Inject constructor( - @ApplicationContext - private val context: Context, + @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val stringConst: StringConst ) { - private var logger: BleLoggerAndLauncher? = null + private var tree: nRFLoggerTree? = null private val _wheelSize = MutableStateFlow(WheelSizes.default) internal val wheelSize = _wheelSize.asStateFlow() @@ -92,9 +92,12 @@ class CSCRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "CSC", device.address) + tree = nRFLoggerTree(context, "CSC", device.address, device.name) + .apply { setLoggingTagsEnabled(false) } + .also { Timber.plant(it) } + _data.value = _data.value.copy(deviceName = device.name) - serviceManager.startService(CSCService::class.java, device) + serviceManager.startService(CSCService::class.java, device, context.getString(R.string.csc_title_short)) } internal fun setSpeedUnit(speedUnit: SpeedUnit) { @@ -123,11 +126,7 @@ class CSCRepository @Inject constructor( } fun openLogger() { - logger?.launch() - } - - fun log(priority: Int, message: String) { - logger?.log(priority, message) + LoggerLauncher.launch(context, tree?.session as? LogSession) } fun disconnect() { @@ -135,7 +134,8 @@ class CSCRepository @Inject constructor( } private fun clean() { - logger = null + tree?.let { Timber.uproot(it) } + tree = null _data.value = CSCServiceData() } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index 8c62e19af..fe5a8fb72 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -51,7 +51,7 @@ import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.kotlin.ble.profile.csc.CSCDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import java.util.* +import java.util.UUID import javax.inject.Inject val CSC_SERVICE_UUID: UUID = UUID.fromString("00001816-0000-1000-8000-00805f9b34fb") @@ -86,7 +86,7 @@ internal class CSCService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val client = ClientBleGatt.connect(this@CSCService, device, lifecycleScope, logger = { p, s -> repository.log(p, s) }) + val client = ClientBleGatt.connect(this@CSCService, device, lifecycleScope) this@CSCService.client = client client.connectionStateWithStatus diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt index 2140926a8..08c48cabc 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCContentView.kt @@ -47,7 +47,7 @@ import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import no.nordicsemi.android.common.theme.view.RadioButtonGroup +import no.nordicsemi.android.common.ui.view.RadioButtonGroup import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.data.CSCServiceData import no.nordicsemi.android.csc.data.SpeedUnit diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt index e87358f2f..23949df9b 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/view/CSCMappers.kt @@ -31,8 +31,8 @@ package no.nordicsemi.android.csc.view -import no.nordicsemi.android.common.theme.view.RadioButtonItem -import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity +import no.nordicsemi.android.common.ui.view.RadioButtonItem +import no.nordicsemi.android.common.ui.view.RadioGroupViewEntity import no.nordicsemi.android.csc.data.SpeedUnit import no.nordicsemi.android.kotlin.ble.profile.csc.data.CSCData import java.util.Locale @@ -52,29 +52,29 @@ internal fun CSCData.speedWithSpeedUnit(speedUnit: SpeedUnit): Float { internal fun CSCData.displaySpeed(speedUnit: SpeedUnit): String { val speedWithUnit = speedWithSpeedUnit(speedUnit) return when (speedUnit) { - 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) + SpeedUnit.M_S -> String.format(Locale.US, "%.1f m/s", speedWithUnit) + SpeedUnit.KM_H -> String.format(Locale.US, "%.1f km/h", speedWithUnit) + SpeedUnit.MPH -> String.format(Locale.US, "%.1f mph", speedWithUnit) } } internal fun CSCData.displayCadence(): String { - return String.format("%.0f RPM", cadence) + return String.format(Locale.US, "%.0f RPM", cadence) } internal fun CSCData.displayDistance(speedUnit: SpeedUnit): String { return when (speedUnit) { - SpeedUnit.M_S -> String.format("%.0f m", distance) - SpeedUnit.KM_H -> String.format("%.0f m", distance) - SpeedUnit.MPH -> String.format("%.0f yd", distance.toYards()) + SpeedUnit.M_S -> String.format(Locale.US, "%.0f m", distance) + SpeedUnit.KM_H -> String.format(Locale.US, "%.0f m", distance) + SpeedUnit.MPH -> String.format(Locale.US, "%.0f yd", distance.toYards()) } } internal fun CSCData.displayTotalDistance(speedUnit: SpeedUnit): String { return when (speedUnit) { - SpeedUnit.M_S -> String.format("%.2f m", totalDistance) - SpeedUnit.KM_H -> String.format("%.2f km", totalDistance.toKilometers()) - SpeedUnit.MPH -> String.format("%.2f mile", totalDistance.toMiles()) + SpeedUnit.M_S -> String.format(Locale.US, "%.2f m", totalDistance) + SpeedUnit.KM_H -> String.format(Locale.US, "%.2f km", totalDistance.toKilometers()) + SpeedUnit.MPH -> String.format(Locale.US, "%.2f mile", totalDistance.toMiles()) } } @@ -93,7 +93,7 @@ internal fun String.toSpeedUnit(): SpeedUnit { internal fun SpeedUnit.temperatureSettingsItems(): RadioGroupViewEntity { return RadioGroupViewEntity( - SpeedUnit.values().map { createRadioButtonItem(it, this) } + SpeedUnit.entries.map { createRadioButtonItem(it, this) } ) } @@ -110,14 +110,14 @@ private fun displayTemperature(unit: SpeedUnit): String { } private fun Float.toYards(): Float { - return this*1.0936f + return this * 1.0936f } private fun Float.toKilometers(): Float { - return this/1000f + return this / 1000f } private fun Float.toMiles(): Float { - return this*0.0006f + return this * 0.0006f } diff --git a/profile_csc/src/main/res/values/strings.xml b/profile_csc/src/main/res/values/strings.xml index 3596909ad..9ecce10ff 100644 --- a/profile_csc/src/main/res/values/strings.xml +++ b/profile_csc/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ Cyclic and speed cadence + CSC Select wheel size diff --git a/profile_gls/build.gradle.kts b/profile_gls/build.gradle.kts index b684a5d1e..9e9337437 100644 --- a/profile_gls/build.gradle.kts +++ b/profile_gls/build.gradle.kts @@ -45,9 +45,10 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) + implementation(libs.nordic.logger) implementation(libs.nordic.navigation) - implementation(libs.nordic.uilogger) implementation(libs.nordic.blek.client) implementation(libs.nordic.blek.profile) @@ -73,7 +74,7 @@ dependencies { testImplementation(libs.test.mockk) testImplementation(libs.androidx.test.ext) testImplementation(libs.kotlinx.coroutines.test) - testImplementation(libs.test.slf4j.simple) + testImplementation(libs.slf4j.simple) testImplementation(libs.test.robolectric) testImplementation(libs.kotlin.junit) } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSServer.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSServer.kt index c6cce20db..8799c40f2 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSServer.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/GLSServer.kt @@ -3,16 +3,12 @@ package no.nordicsemi.android.gls import android.annotation.SuppressLint import android.content.Context import android.os.ParcelUuid -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.core.DataByteArray -import no.nordicsemi.android.common.logger.BleLogger -import no.nordicsemi.android.common.logger.DefaultConsoleLogger import no.nordicsemi.android.gls.main.viewmodel.BATTERY_LEVEL_CHARACTERISTIC_UUID import no.nordicsemi.android.gls.main.viewmodel.BATTERY_SERVICE_UUID import no.nordicsemi.android.gls.main.viewmodel.GLS_SERVICE_UUID @@ -26,6 +22,7 @@ import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertisingData import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertisingSettings import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty +import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray import no.nordicsemi.android.kotlin.ble.profile.gls.RecordAccessControlPointInputParser import no.nordicsemi.android.kotlin.ble.server.main.ServerBleGatt import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattCharacteristic @@ -42,9 +39,6 @@ private const val STANDARD_DELAY = 1000L @Singleton class GLSServer @Inject constructor( private val scope: CoroutineScope, - @ApplicationContext - private val context: Context, - private val logger: BleLogger = DefaultConsoleLogger(context), ) { private lateinit var server: ServerBleGatt @@ -197,7 +191,6 @@ class GLSServer @Inject constructor( config = arrayOf(serviceConfig, batteryService), mock = device, scope = scope, - logger = { _, log -> println(log) } ) BleAdvertiser.create(context) diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsScreen.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsScreen.kt index 67c0e49ec..6e7678b4a 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsScreen.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/details/view/GLSDetailsScreen.kt @@ -39,7 +39,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import no.nordicsemi.android.gls.R import no.nordicsemi.android.gls.details.viewmodel.GLSDetailsViewModel -import no.nordicsemi.android.ui.view.LoggerBackIconAppBar +import no.nordicsemi.android.ui.view.CloseIconAppBar @Composable internal fun GLSDetailsScreen() { @@ -47,7 +47,7 @@ internal fun GLSDetailsScreen() { val record by viewModel.record.collectAsStateWithLifecycle() Column { - LoggerBackIconAppBar(stringResource(id = R.string.gls_title)) { + CloseIconAppBar(stringResource(id = R.string.gls_title)) { viewModel.navigateBack() } diff --git a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt index aaa32f2e5..64acdc7df 100644 --- a/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt +++ b/profile_gls/src/main/java/no/nordicsemi/android/gls/main/viewmodel/GLSViewModel.kt @@ -50,7 +50,7 @@ import kotlinx.coroutines.launch import no.nordicsemi.android.analytics.AppAnalytics import no.nordicsemi.android.analytics.Profile import no.nordicsemi.android.analytics.ProfileConnectedEvent -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.LoggerLauncher import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.GlsDetailsDestinationId @@ -80,22 +80,22 @@ import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus import no.nordicsemi.android.kotlin.ble.profile.gls.data.ResponseData import no.nordicsemi.android.kotlin.ble.profile.racp.RACPOpCode import no.nordicsemi.android.kotlin.ble.profile.racp.RACPResponseCode +import no.nordicsemi.android.log.LogSession +import no.nordicsemi.android.log.timber.nRFLoggerTree import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId -import no.nordicsemi.android.ui.view.NordicLoggerFactory -import no.nordicsemi.android.ui.view.StringConst import no.nordicsemi.android.utils.tryOrLog +import timber.log.Timber import java.util.* import javax.inject.Inject val GLS_SERVICE_UUID: UUID = UUID.fromString("00001808-0000-1000-8000-00805f9b34fb") -val GLUCOSE_MEASUREMENT_CHARACTERISTIC = UUID.fromString("00002A18-0000-1000-8000-00805f9b34fb") -val GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC = UUID.fromString("00002A34-0000-1000-8000-00805f9b34fb") -val GLUCOSE_FEATURE_CHARACTERISTIC = UUID.fromString("00002A51-0000-1000-8000-00805f9b34fb") -val RACP_CHARACTERISTIC = UUID.fromString("00002A52-0000-1000-8000-00805f9b34fb") +val GLUCOSE_MEASUREMENT_CHARACTERISTIC: UUID = UUID.fromString("00002A18-0000-1000-8000-00805f9b34fb") +val GLUCOSE_MEASUREMENT_CONTEXT_CHARACTERISTIC: UUID = UUID.fromString("00002A34-0000-1000-8000-00805f9b34fb") +val RACP_CHARACTERISTIC: UUID = UUID.fromString("00002A52-0000-1000-8000-00805f9b34fb") -val BATTERY_SERVICE_UUID = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb") -val BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb") +val BATTERY_SERVICE_UUID: UUID = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb") +val BATTERY_LEVEL_CHARACTERISTIC_UUID: UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb") @SuppressLint("MissingPermission") @HiltViewModel @@ -103,12 +103,8 @@ internal class GLSViewModel @Inject constructor( @ApplicationContext context: Context, private val navigationManager: Navigator, private val analytics: AppAnalytics, - private val stringConst: StringConst, - private val loggerFactory: NordicLoggerFactory ) : AndroidViewModel(context as Application) { - private var client: ClientBleGatt? = null - private lateinit var logger: BleLoggerAndLauncher private lateinit var glucoseMeasurementCharacteristic: ClientBleGattCharacteristic private lateinit var recordAccessControlPointCharacteristic: ClientBleGattCharacteristic @@ -116,6 +112,8 @@ internal class GLSViewModel @Inject constructor( private val _state = MutableStateFlow(GLSViewState()) val state = _state.asStateFlow() + private var tree: nRFLoggerTree? = null + private val highestSequenceNumber get() = state.value.glsServiceData.records.keys.maxByOrNull { it.sequenceNumber }?.sequenceNumber ?: -1 @@ -127,6 +125,12 @@ internal class GLSViewModel @Inject constructor( .launchIn(viewModelScope) } + override fun onCleared() { + super.onCleared() + tree?.let { Timber.uproot(it) } + tree = null + } + internal fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() @@ -136,7 +140,7 @@ internal class GLSViewModel @Inject constructor( fun onEvent(event: GLSScreenViewEvent) { when (event) { - OpenLoggerEvent -> logger.launch() + OpenLoggerEvent -> openLogger() is OnWorkingModeSelected -> onEvent(event) is OnGLSRecordClick -> navigateToDetails(event.record) DisconnectEvent -> onDisconnectEvent() @@ -168,9 +172,11 @@ internal class GLSViewModel @Inject constructor( private fun startGattClient(device: ServerDevice) = viewModelScope.launch { _state.value = _state.value.copy(deviceName = device.name) - logger = loggerFactory.createNordicLogger(getApplication(), stringConst.APP_NAME, "GLS", device.address) + tree = nRFLoggerTree(getApplication(), "GLS", device.address, device.name) + .apply { setLoggingTagsEnabled(false) } + .also { Timber.plant(it) } - val client = ClientBleGatt.connect(getApplication(), device, viewModelScope, logger = logger) + val client = ClientBleGatt.connect(getApplication(), device, viewModelScope) this@GLSViewModel.client = client client.waitForBonding() @@ -297,6 +303,10 @@ internal class GLSViewModel @Inject constructor( } } + private fun openLogger() { + LoggerLauncher.launch(getApplication(), tree?.session as? LogSession) + } + private fun clear() { _state.value = _state.value.copyAndClear() } diff --git a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt index 3097cda71..44db964f6 100644 --- a/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt +++ b/profile_gls/src/test/java/no/nordicsemi/android/gls/GLSViewModelTest.kt @@ -3,8 +3,6 @@ package no.nordicsemi.android.gls import android.content.Context import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltTestApplication -import io.mockk.every -import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.RelaxedMockK import io.mockk.junit4.MockKRule import io.mockk.justRun @@ -19,8 +17,6 @@ import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import no.nordicsemi.android.analytics.AppAnalytics -import no.nordicsemi.android.common.core.ApplicationScope -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.gls.data.WorkingMode @@ -32,16 +28,14 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.gls.GlucoseMeasurementParser import no.nordicsemi.android.kotlin.ble.profile.gls.data.RequestStatus -import no.nordicsemi.android.ui.view.NordicLoggerFactory -import no.nordicsemi.android.ui.view.StringConst import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule import org.junit.Test -import org.robolectric.annotation.Config import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config import kotlin.test.assertContentEquals /** @@ -63,15 +57,9 @@ internal class GLSViewModelTest { @RelaxedMockK lateinit var analytics: AppAnalytics - @MockK - lateinit var stringConst: StringConst - @RelaxedMockK lateinit var context: Context - @RelaxedMockK - lateinit var logger: BleLoggerAndLauncher - lateinit var viewModel: GLSViewModel lateinit var glsServer: GLSServer @@ -95,24 +83,11 @@ internal class GLSViewModelTest { fun before() { runBlocking { mockkStatic("no.nordicsemi.android.common.core.ApplicationScopeKt") - every { ApplicationScope } returns CoroutineScope(UnconfinedTestDispatcher()) - every { stringConst.APP_NAME } returns "Test" - - viewModel = spyk(GLSViewModel(context, navigator, analytics, stringConst, object : - NordicLoggerFactory { - override fun createNordicLogger( - context: Context, - profile: String?, - key: String, - name: String?, - ): BleLoggerAndLauncher { - return logger - } - - })) + + viewModel = spyk(GLSViewModel(context, navigator, analytics)) justRun { viewModel.logAnalytics(any()) } - glsServer = GLSServer(CoroutineScope(UnconfinedTestDispatcher()), context, logger) + glsServer = GLSServer(CoroutineScope(UnconfinedTestDispatcher())) glsServer.start(spyk(), device) } } diff --git a/profile_hrs/build.gradle.kts b/profile_hrs/build.gradle.kts index 4a3c80a45..eca01abbf 100644 --- a/profile_hrs/build.gradle.kts +++ b/profile_hrs/build.gradle.kts @@ -45,10 +45,10 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation(libs.nordic.core) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) implementation(libs.nordic.navigation) - implementation(libs.nordic.uilogger) + implementation(libs.nordic.logger) implementation(libs.nordic.blek.client) implementation(libs.nordic.blek.profile) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 220bd8439..fc6eb8639 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -37,28 +37,28 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map -import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.common.logger.DefaultBleLogger +import no.nordicsemi.android.common.logger.LoggerLauncher +import no.nordicsemi.android.hrs.R import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hrs.data.HRSData +import no.nordicsemi.android.log.LogSession +import no.nordicsemi.android.log.timber.nRFLoggerTree import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst +import no.nordicsemi.android.utils.simpleSharedFlow +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton class HRSRepository @Inject constructor( - @ApplicationContext - private val context: Context, + @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val stringConst: StringConst ) { - private var logger: BleLoggerAndLauncher? = null + private var tree: nRFLoggerTree? = null private val _data = MutableStateFlow(HRSServiceData()) internal val data = _data.asStateFlow() @@ -86,9 +86,12 @@ class HRSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "HRS", device.address) + tree = nRFLoggerTree(context, "HRS", device.address, device.name) + .apply { setLoggingTagsEnabled(false) } + .also { Timber.plant(it) } + _data.value = _data.value.copy(deviceName = device.name) - serviceManager.startService(HRSService::class.java, device) + serviceManager.startService(HRSService::class.java, device, context.getString(R.string.hrs_title)) } fun switchZoomIn() { @@ -117,11 +120,7 @@ class HRSRepository @Inject constructor( } fun openLogger() { - logger?.launch() - } - - fun log(priority: Int, message: String) { - logger?.log(priority, message) + LoggerLauncher.launch(context, tree?.session as? LogSession) } fun disconnect() { @@ -129,7 +128,8 @@ class HRSRepository @Inject constructor( } private fun clean() { - logger = null + tree?.let { Timber.uproot(it) } + tree = null _data.value = HRSServiceData() } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 0855a0c09..b17857746 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -88,7 +88,7 @@ internal class HRSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val client = ClientBleGatt.connect(this@HRSService, device, lifecycleScope, logger = { p, s -> repository.log(p, s) }) + val client = ClientBleGatt.connect(this@HRSService, device, lifecycleScope) this@HRSService.client = client client.waitForBonding() diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index 991aed56d..96dbea61c 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -35,7 +35,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState diff --git a/profile_hts/build.gradle.kts b/profile_hts/build.gradle.kts index 1f4b79cf6..69c0cef4c 100644 --- a/profile_hts/build.gradle.kts +++ b/profile_hts/build.gradle.kts @@ -49,10 +49,10 @@ dependencies { implementation(libs.nordic.blek.profile) implementation(libs.nordic.blek.uiscanner) - implementation(libs.nordic.core) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) implementation(libs.nordic.navigation) - implementation(libs.nordic.uilogger) + implementation(libs.nordic.logger) implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt index b84003b2c..869d5f171 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSRepository.kt @@ -37,29 +37,29 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map -import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.common.logger.DefaultBleLogger +import no.nordicsemi.android.common.logger.LoggerLauncher +import no.nordicsemi.android.hts.R import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.hts.view.TemperatureUnit import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.hts.data.HTSData +import no.nordicsemi.android.log.LogSession +import no.nordicsemi.android.log.timber.nRFLoggerTree import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst +import no.nordicsemi.android.utils.simpleSharedFlow +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton class HTSRepository @Inject constructor( - @ApplicationContext - private val context: Context, + @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val stringConst: StringConst ) { - private var logger: BleLoggerAndLauncher? = null + private var tree: nRFLoggerTree? = null private val _data = MutableStateFlow(HTSServiceData()) internal val data = _data.asStateFlow() @@ -87,9 +87,12 @@ class HTSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { + tree = nRFLoggerTree(context, "HTS", device.address, device.name) + .apply { setLoggingTagsEnabled(false) } + .also { Timber.plant(it) } + _data.value = _data.value.copy(deviceName = device.name) - logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "HTS", device.address) - serviceManager.startService(HTSService::class.java, device) + serviceManager.startService(HTSService::class.java, device, context.getString(R.string.hts_title)) } internal fun setTemperatureUnit(temperatureUnit: TemperatureUnit) { @@ -109,11 +112,7 @@ class HTSRepository @Inject constructor( } fun openLogger() { - logger?.launch() - } - - fun log(priority: Int, message: String) { - logger?.log(priority, message) + LoggerLauncher.launch(context, tree?.session as? LogSession) } fun disconnect() { @@ -126,7 +125,8 @@ class HTSRepository @Inject constructor( } private fun clean() { - logger = null + tree?.let { Timber.uproot(it) } + tree = null _data.value = HTSServiceData() } } diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt index 460246b57..1b40888c1 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/repository/HTSService.kt @@ -86,7 +86,7 @@ internal class HTSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val client = ClientBleGatt.connect(this@HTSService, device, lifecycleScope, logger = { p, s -> repository.log(p, s) }) + val client = ClientBleGatt.connect(this@HTSService, device, lifecycleScope) this@HTSService.client = client client.connectionStateWithStatus diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt index 4851bbc5d..a37944394 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSContentView.kt @@ -43,7 +43,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import no.nordicsemi.android.common.theme.view.RadioButtonGroup +import no.nordicsemi.android.common.ui.view.RadioButtonGroup import no.nordicsemi.android.hts.R import no.nordicsemi.android.hts.data.HTSServiceData import no.nordicsemi.android.ui.view.BatteryLevelView diff --git a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSMapper.kt b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSMapper.kt index 7b1ebc0f6..14d15ccf0 100644 --- a/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSMapper.kt +++ b/profile_hts/src/main/java/no/nordicsemi/android/hts/view/HTSMapper.kt @@ -31,8 +31,9 @@ package no.nordicsemi.android.hts.view -import no.nordicsemi.android.common.theme.view.RadioButtonItem -import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity +import no.nordicsemi.android.common.ui.view.RadioButtonItem +import no.nordicsemi.android.common.ui.view.RadioGroupViewEntity +import java.util.Locale private const val DISPLAY_FAHRENHEIT = "°F" private const val DISPLAY_CELSIUS = "°C" @@ -40,9 +41,9 @@ private const val DISPLAY_KELVIN = "°K" internal fun displayTemperature(value: Float, temperatureUnit: TemperatureUnit): String { return when (temperatureUnit) { - TemperatureUnit.CELSIUS -> String.format("%.1f °C", value) - TemperatureUnit.FAHRENHEIT -> String.format("%.1f °F", value * 1.8f + 32f) - TemperatureUnit.KELVIN -> String.format("%.1f °K", value + 273.15f) + TemperatureUnit.CELSIUS -> String.format(Locale.US, "%.1f °C", value) + TemperatureUnit.FAHRENHEIT -> String.format(Locale.US, "%.1f °F", value * 1.8f + 32f) + TemperatureUnit.KELVIN -> String.format(Locale.US, "%.1f °K", value + 273.15f) } } @@ -57,7 +58,7 @@ internal fun String.toTemperatureUnit(): TemperatureUnit { internal fun TemperatureUnit.temperatureSettingsItems(): RadioGroupViewEntity { return RadioGroupViewEntity( - TemperatureUnit.values().map { createRadioButtonItem(it, this) } + TemperatureUnit.entries.map { createRadioButtonItem(it, this) } ) } diff --git a/profile_prx/build.gradle.kts b/profile_prx/build.gradle.kts index 2b39270f1..89559acc1 100644 --- a/profile_prx/build.gradle.kts +++ b/profile_prx/build.gradle.kts @@ -50,10 +50,10 @@ dependencies { implementation(libs.nordic.blek.server) implementation(libs.nordic.blek.uiscanner) - implementation(libs.nordic.core) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) implementation(libs.nordic.navigation) - implementation(libs.nordic.uilogger) + implementation(libs.nordic.logger) implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt index 7787532f6..74fbe8916 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/data/PRXServiceData.kt @@ -14,7 +14,6 @@ data class PRXServiceData( val deviceName: String? = null, val missingServices: Boolean = false ) { - val disconnectStatus = if (missingServices) { BleGattConnectionStatus.NOT_SUPPORTED } else { diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt index cb9098073..8f568d36e 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/AlarmHandler.kt @@ -60,12 +60,11 @@ internal class AlarmHandler @Inject constructor( } fun playAlarm(alarmLevel: AlarmLevel) { - if (alarmLevel == AlarmLevel.NONE) { - pauseAlarm() - return - } val ringtone = when (alarmLevel) { - AlarmLevel.NONE -> null + AlarmLevel.NONE -> { + pauseAlarm() + return + } AlarmLevel.MEDIUM -> mediumLevelRingtone AlarmLevel.HIGH -> highLevelRingtone } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt index 9d790899e..8976c6764 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXRepository.kt @@ -37,28 +37,28 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map -import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.common.logger.DefaultBleLogger +import no.nordicsemi.android.common.logger.LoggerLauncher import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.prx.AlarmLevel +import no.nordicsemi.android.log.LogSession +import no.nordicsemi.android.log.timber.nRFLoggerTree +import no.nordicsemi.android.prx.R import no.nordicsemi.android.prx.data.PRXServiceData import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst +import no.nordicsemi.android.utils.simpleSharedFlow +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton class PRXRepository @Inject internal constructor( - @ApplicationContext - private val context: Context, + @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val stringConst: StringConst ) { - private var logger: BleLoggerAndLauncher? = null + private var tree: nRFLoggerTree? = null private val _data = MutableStateFlow(PRXServiceData()) internal val data = _data.asStateFlow() @@ -89,9 +89,12 @@ class PRXRepository @Inject internal constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "PRX", device.address) + tree = nRFLoggerTree(context, "Proximity", device.address, device.name) + .apply { setLoggingTagsEnabled(false) } + .also { Timber.plant(it) } + _data.value = _data.value.copy(deviceName = device.name) - serviceManager.startService(PRXService::class.java, device) + serviceManager.startService(PRXService::class.java, device, context.getString(R.string.prx_title)) } fun onConnectionStateChanged(connection: GattConnectionStateWithStatus) { @@ -119,11 +122,7 @@ class PRXRepository @Inject internal constructor( } fun openLogger() { - logger?.launch() - } - - fun log(priority: Int, message: String) { - logger?.log(priority, message) + LoggerLauncher.launch(context, tree?.session as? LogSession) } fun onMissingServices() { @@ -137,7 +136,8 @@ class PRXRepository @Inject internal constructor( } private fun clean() { - logger = null + tree?.let { Timber.uproot(it) } + tree = null _data.value = PRXServiceData() } } diff --git a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt index 7d916c576..e7c2723e6 100644 --- a/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt +++ b/profile_prx/src/main/java/no/nordicsemi/android/prx/repository/PRXService.kt @@ -164,7 +164,6 @@ internal class PRXService : NotificationService() { this@PRXService, device, lifecycleScope, - logger = { p, s -> repository.log(p, s) }, options = BleGattConnectOptions(autoConnect = true) ) this@PRXService.client = client diff --git a/profile_rscs/build.gradle.kts b/profile_rscs/build.gradle.kts index 03a732003..f98f99656 100644 --- a/profile_rscs/build.gradle.kts +++ b/profile_rscs/build.gradle.kts @@ -45,10 +45,10 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation(libs.nordic.core) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) implementation(libs.nordic.navigation) - implementation(libs.nordic.uilogger) + implementation(libs.nordic.logger) implementation(libs.nordic.blek.client) implementation(libs.nordic.blek.profile) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 53861d4fe..e2120c9b4 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -37,28 +37,28 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map -import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.common.logger.DefaultBleLogger +import no.nordicsemi.android.common.logger.LoggerLauncher import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.kotlin.ble.profile.rscs.data.RSCSData +import no.nordicsemi.android.log.LogSession +import no.nordicsemi.android.log.timber.nRFLoggerTree +import no.nordicsemi.android.rscs.R import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager -import no.nordicsemi.android.ui.view.StringConst +import no.nordicsemi.android.utils.simpleSharedFlow +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton class RSCSRepository @Inject constructor( - @ApplicationContext - private val context: Context, + @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, - private val stringConst: StringConst ) { - private var logger: BleLoggerAndLauncher? = null + private var tree: nRFLoggerTree? = null private val _data = MutableStateFlow(RSCSServiceData()) internal val data = _data.asStateFlow() @@ -86,9 +86,12 @@ class RSCSRepository @Inject constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "RSCS", device.address) + tree = nRFLoggerTree(context, "RSC", device.address, device.name) + .apply { setLoggingTagsEnabled(false) } + .also { Timber.plant(it) } + _data.value = _data.value.copy(deviceName = device.name) - serviceManager.startService(RSCSService::class.java, device) + serviceManager.startService(RSCSService::class.java, device, context.getString(R.string.rscs_title_short)) } fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { @@ -109,11 +112,7 @@ class RSCSRepository @Inject constructor( } fun openLogger() { - logger?.launch() - } - - fun log(priority: Int, message: String) { - logger?.log(priority, message) + LoggerLauncher.launch(context, tree?.session as? LogSession) } fun disconnect() { @@ -121,7 +120,8 @@ class RSCSRepository @Inject constructor( } private fun clean() { - logger = null + tree?.let { Timber.uproot(it) } + tree = null _data.value = RSCSServiceData() } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index 64a829922..a7f9a45a5 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -86,7 +86,7 @@ internal class RSCSService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val client = ClientBleGatt.connect(this@RSCSService, device, lifecycleScope, logger = { p, s -> repository.log(p, s) }) + val client = ClientBleGatt.connect(this@RSCSService, device, lifecycleScope) this@RSCSService.client = client client.connectionStateWithStatus diff --git a/profile_rscs/src/main/res/values/strings.xml b/profile_rscs/src/main/res/values/strings.xml index eb061fc43..4513ed11b 100644 --- a/profile_rscs/src/main/res/values/strings.xml +++ b/profile_rscs/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ Running speed & cadence + RSC Activity Pace diff --git a/profile_uart/build.gradle.kts b/profile_uart/build.gradle.kts index cf1bd0335..b6a9b2bdf 100644 --- a/profile_uart/build.gradle.kts +++ b/profile_uart/build.gradle.kts @@ -50,10 +50,10 @@ dependencies { implementation(project(":lib_ui")) implementation(project(":lib_utils")) - implementation(libs.nordic.core) + implementation(libs.nordic.ui) implementation(libs.nordic.theme) implementation(libs.nordic.navigation) - implementation(libs.nordic.uilogger) + implementation(libs.nordic.logger) implementation(libs.nordic.blek.client) implementation(libs.nordic.blek.profile) @@ -62,12 +62,8 @@ dependencies { implementation(libs.nordic.blek.advertiser) implementation(libs.nordic.blek.uiscanner) - implementation(libs.room.runtime) implementation(libs.room.ktx) - ksp(libs.room.compiler) - - implementation(libs.accompanist.pager) - implementation(libs.accompanist.pagerindicators) + kapt(libs.room.compiler) implementation(libs.androidx.dataStore.core) implementation(libs.androidx.dataStore.preferences) @@ -79,6 +75,9 @@ dependencies { implementation(libs.androidx.hilt.navigation.compose) + // XML Serialization + implementation("io.github.pdvrieze.xmlutil:serialization-android:0.90.1") + testImplementation(libs.hilt.android.testing) kaptTest(libs.hilt.compiler) testImplementation(libs.androidx.test.rules) @@ -87,12 +86,6 @@ dependencies { testImplementation(libs.test.mockk) testImplementation(libs.androidx.test.ext) testImplementation(libs.kotlinx.coroutines.test) - testImplementation(libs.test.slf4j.simple) testImplementation(libs.test.robolectric) testImplementation(libs.kotlin.junit) - - implementation("org.simpleframework:simple-xml:2.7.1") { - exclude(group = "stax", module = "stax-api") - exclude(group = "xpp3", module = "xpp3") - } } diff --git a/profile_uart/module-rules.pro b/profile_uart/module-rules.pro index 4bc45e71d..7e9d74c9f 100644 --- a/profile_uart/module-rules.pro +++ b/profile_uart/module-rules.pro @@ -14,7 +14,4 @@ # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; -#} - --keep class no.nordicsemi.android.uart.db.XmlConfiguration{ *; } --keep class no.nordicsemi.android.uart.db.XmlMacro{ *; } +#} \ No newline at end of file diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/UartServer.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/UartServer.kt index 1a55aa69a..c5a53298e 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/UartServer.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/UartServer.kt @@ -9,13 +9,13 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.advertiser.BleAdvertiser import no.nordicsemi.android.kotlin.ble.core.MockServerDevice import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertisingConfig import no.nordicsemi.android.kotlin.ble.core.advertiser.BleAdvertisingData import no.nordicsemi.android.kotlin.ble.core.data.BleGattPermission import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty +import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray import no.nordicsemi.android.kotlin.ble.server.main.ServerBleGatt import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattCharacteristic import no.nordicsemi.android.kotlin.ble.server.main.service.ServerBleGattCharacteristicConfig @@ -37,7 +37,6 @@ private const val STANDARD_DELAY = 1000L class UartServer @Inject constructor( private val scope: CoroutineScope, ) { - private lateinit var server: ServerBleGatt private lateinit var rxCharacteristic: ServerBleGattCharacteristic @@ -127,11 +126,7 @@ class UartServer @Inject constructor( private fun startBatteryService() { scope.launch { repeat(100) { - send(batteryLevelCharacteristic, DataByteArray.from(0x61)) - delay(STANDARD_DELAY) - send(batteryLevelCharacteristic, DataByteArray.from(0x60)) - delay(STANDARD_DELAY) - send(batteryLevelCharacteristic, DataByteArray.from(0x5F)) + send(batteryLevelCharacteristic, DataByteArray.from(it.toByte())) delay(STANDARD_DELAY) } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/ConfigurationDataSource.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/ConfigurationDataSource.kt index 3b0e4ddf3..45a09b99a 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/ConfigurationDataSource.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/ConfigurationDataSource.kt @@ -47,8 +47,7 @@ private const val LAST_CONFIGURATION_KEY = "LAST_CONFIGURATION" @Singleton internal class ConfigurationDataSource @Inject constructor( - @ApplicationContext - private val context: Context + @ApplicationContext private val context: Context ) { private val Context.dataStore: DataStore by preferencesDataStore(name = FILE) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroIcon.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroIcon.kt index 0e7613499..79e633fc3 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroIcon.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/MacroIcon.kt @@ -31,7 +31,7 @@ package no.nordicsemi.android.uart.data -enum class MacroIcon(public val index: Int) { +enum class MacroIcon(val index: Int) { LEFT(0), UP(1), RIGHT(2), @@ -55,7 +55,7 @@ enum class MacroIcon(public val index: Int) { companion object { fun create(index: Int): MacroIcon { - return values().firstOrNull { it.index == index } + return entries.firstOrNull { it.index == index } ?: throw IllegalArgumentException("Cannot create MacroIcon for index: $index") } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTPersistentDataSource.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTPersistentDataSource.kt index b23f83bf0..b9b557c4d 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTPersistentDataSource.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/data/UARTPersistentDataSource.kt @@ -33,18 +33,13 @@ package no.nordicsemi.android.uart.data import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import no.nordicsemi.android.uart.db.CommentVisitor +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import nl.adaptivity.xmlutil.serialization.XML import no.nordicsemi.android.uart.db.Configuration import no.nordicsemi.android.uart.db.ConfigurationsDao import no.nordicsemi.android.uart.db.XmlConfiguration import no.nordicsemi.android.uart.db.XmlMacro -import org.simpleframework.xml.Serializer -import org.simpleframework.xml.core.Persister -import org.simpleframework.xml.strategy.Strategy -import org.simpleframework.xml.strategy.VisitorStrategy -import org.simpleframework.xml.stream.Format -import org.simpleframework.xml.stream.HyphenStyle -import java.io.StringWriter import javax.inject.Inject import javax.inject.Singleton @@ -52,22 +47,24 @@ import javax.inject.Singleton internal class UARTPersistentDataSource @Inject constructor( private val configurationsDao: ConfigurationsDao, ) { - - fun getConfigurations(): Flow> = configurationsDao.load().map { - it.mapNotNull { it.toDomain() } + private val serializer = XML { + recommended() } + fun getConfigurations(): Flow> = configurationsDao.load() + .map { list -> + list.mapNotNull { it.toDomain() } + } + private fun Configuration.toDomain(): UARTConfiguration? { return try { val xml: String = xml - val format = Format(HyphenStyle()) - val serializer: Serializer = Persister(format) - val configuration = serializer.read(XmlConfiguration::class.java, xml) + val configuration = serializer.decodeFromString(xml) UARTConfiguration( _id, - configuration.name ?: "Unknown", - createMacro(configuration.commands) + configuration.name, + createMacro(configuration.commands.commands) ) } catch (t: Throwable) { t.printStackTrace() @@ -75,24 +72,16 @@ internal class UARTPersistentDataSource @Inject constructor( } } - private fun createMacro(macros: Array): List { + private fun createMacro(macros: Array): List { return macros.map { - if (it == null) { - null - } else { - val icon = MacroIcon.create(it.iconIndex) - UARTMacro(icon, it.command, it.eol) - } + if (it.command == null) return@map null + val icon = MacroIcon.create(it.iconIndex) + UARTMacro(icon, it.command, it.eol) } } suspend fun saveConfiguration(configuration: UARTConfiguration) { - val format = Format(HyphenStyle()) - val strategy: Strategy = VisitorStrategy(CommentVisitor()) - val serializer: Serializer = Persister(strategy, format) - val writer = StringWriter() - serializer.write(configuration.toXmlConfiguration(), writer) - val xml = writer.toString() + val xml = serializer.encodeToString(configuration.toXmlConfiguration()) configurationsDao.insert(Configuration(configuration.id, configuration.name, xml, 0)) } @@ -107,13 +96,13 @@ internal class UARTPersistentDataSource @Inject constructor( val commands = macros.map { macro -> macro?.let { XmlMacro().apply { - setEol(it.newLineChar.index) + eolIndex = it.newLineChar.index command = it.command iconIndex = it.icon.index } - } + } ?: XmlMacro() }.toTypedArray() - xmlConfiguration.commands = commands + xmlConfiguration.commands = XmlConfiguration.Commands(commands = commands) return xmlConfiguration } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/CommentVisitor.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/CommentVisitor.kt deleted file mode 100644 index 999352954..000000000 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/CommentVisitor.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2022, Nordic Semiconductor - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be - * used to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package no.nordicsemi.android.uart.db - -import no.nordicsemi.android.uart.data.MacroIcon -import org.simpleframework.xml.strategy.Type -import org.simpleframework.xml.strategy.Visitor -import org.simpleframework.xml.stream.InputNode -import org.simpleframework.xml.stream.NodeMap -import org.simpleframework.xml.stream.OutputNode - -/** - * The comment visitor will add comments to the XML during saving. - */ -internal class CommentVisitor : Visitor { - override fun read(type: Type, node: NodeMap) { - // do nothing - } - - override fun write(type: Type, node: NodeMap) { - if (type.type == Array::class.java) { - val element = node.node - val builder = - StringBuilder("A configuration must have 9 commands, one for each button.\n Possible icons are:") - for (icon in MacroIcon.values()) builder.append("\n - ") - .append(icon.toString()) - element.comment = builder.toString() - } - } -} diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/InitMigration.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/InitMigration.kt index 5bc8bdf79..2b1705d84 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/InitMigration.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/InitMigration.kt @@ -35,7 +35,7 @@ import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase val MIGRATION_1_2 = object : Migration(1, 2) { - override fun migrate(database: SupportSQLiteDatabase) { + override fun migrate(db: SupportSQLiteDatabase) { // Empty implementation, because the schema isn't changing. } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt index 1691252ed..ae7da9c0d 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlConfiguration.kt @@ -29,56 +29,66 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.uart.db; +package no.nordicsemi.android.uart.db -import org.simpleframework.xml.Attribute; -import org.simpleframework.xml.ElementArray; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.core.PersistenceException; -import org.simpleframework.xml.core.Validate; +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlIgnoreWhitespace +import nl.adaptivity.xmlutil.serialization.XmlSerialName -@Root -public class XmlConfiguration { - public static final int COMMANDS_COUNT = 9; +@Serializable +@XmlSerialName("xml-configuration") +@XmlIgnoreWhitespace +data class XmlConfiguration( + @XmlElement(false) + var name: String = "Unnamed", - @Attribute(required = false, empty = "Unnamed") - private String name; + @XmlElement(true) + var commands: Commands = Commands() +) { - @ElementArray - private XmlMacro[] commands = new XmlMacro[COMMANDS_COUNT]; + @Serializable + @XmlSerialName("commands") + data class Commands( + var commands: Array = arrayOf( + XmlMacro(), + XmlMacro(), + XmlMacro(), + XmlMacro(), + XmlMacro(), + XmlMacro(), + XmlMacro(), + XmlMacro(), + XmlMacro() + ), - /** - * Returns the field name - * - * @return optional name - */ - public String getName() { - return name; - } + @XmlElement(false) + val length: Int = commands.size, + ) { + init { + require(commands.size == COMMANDS_COUNT) { "A Macro must have 9 commands" } + } - /** - * Sets the name to specified value - * @param name the new name - */ - public void setName(final String name) { - this.name = name; - } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false - /** - * Returns the array of commands. There is always 9 of them. - * @return the commands array - */ - public XmlMacro[] getCommands() { - return commands; - } + other as Commands + + if (length != other.length) return false + if (!commands.contentEquals(other.commands)) return false + + return true + } - public void setCommands(XmlMacro[] commands) { - this.commands = commands; + override fun hashCode(): Int { + var result = length + result = 31 * result + commands.contentHashCode() + return result + } } - @Validate - private void validate() throws PersistenceException{ - if (commands == null || commands.length != COMMANDS_COUNT) - throw new PersistenceException("There must be always " + COMMANDS_COUNT + " commands in a configuration."); + companion object { + const val COMMANDS_COUNT = 9 } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.kt index 726b9b346..305e850aa 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/db/XmlMacro.kt @@ -29,98 +29,43 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package no.nordicsemi.android.uart.db; - -import org.simpleframework.xml.Attribute; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.Text; - -import no.nordicsemi.android.uart.data.MacroEol; -import no.nordicsemi.android.uart.data.MacroIcon; - -@Root -public class XmlMacro { - - @Text(required = false) - private String command; - - @Attribute(required = false) - private boolean active = false; - - @Attribute(required = false) - private MacroEol eol = MacroEol.LF; - - @Attribute(required = false) - private MacroIcon icon = MacroIcon.LEFT; - - /** - * Sets the command. - * @param command the command that will be sent to UART device - */ - public void setCommand(final String command) { - this.command = command; - } - - /** - * Sets whether the command is active. - * @param active true to make it active - */ - public void setActive(final boolean active) { - this.active = active; - } - - /** - * Sets the new line type. - * @param eol end of line terminator - */ - public void setEol(final int eol) { - this.eol = MacroEol.values()[eol]; - } - - /** - * Sets the icon index. - * @param index index of the icon. - */ - public void setIconIndex(final int index) { - this.icon = MacroIcon.values()[index]; - } - - /** - * Returns the command that will be sent to UART device. - * @return the command - */ - public String getCommand() { - return command; - } - - /** - * Returns whether the icon is active. - * @return true if it's active - */ - public boolean isActive() { - return active; - } - - /** - * Returns the new line type. - * @return end of line terminator - */ - public MacroEol getEol() { - return eol; - } - - /** - * Returns the icon index. - * @return the icon index - */ - public int getIconIndex() { - return icon.getIndex(); - } - /** - * Returns the EOL index. - * @return the EOL index - */ - public int getEolIndex() { - return eol.getIndex(); - } +package no.nordicsemi.android.uart.db + +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlDefault +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName +import nl.adaptivity.xmlutil.serialization.XmlValue +import no.nordicsemi.android.uart.data.MacroEol +import no.nordicsemi.android.uart.data.MacroIcon + +@Serializable +@XmlSerialName("xml-macro") +data class XmlMacro( + @XmlValue(true) + var command: String? = null, + + @XmlElement(false) + @XmlDefault("false") + var active: Boolean = false, + + @XmlElement(false) + @XmlDefault("LF") + var eol: MacroEol = MacroEol.LF, + + @XmlElement(false) + @XmlDefault("LEFT") + var icon: MacroIcon = MacroIcon.LEFT +) { + var iconIndex: Int + get() = icon.ordinal + set(value) { + icon = MacroIcon.entries.toTypedArray()[value] + } + + var eolIndex: Int + get() = eol.ordinal + set(value) { + eol = MacroEol.entries.toTypedArray()[value] + } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt index 80e197830..4fde14e58 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTRepository.kt @@ -37,13 +37,15 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map -import no.nordicsemi.android.common.core.simpleSharedFlow -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher +import no.nordicsemi.android.common.logger.LoggerLauncher import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus +import no.nordicsemi.android.log.LogSession +import no.nordicsemi.android.log.timber.nRFLoggerTree import no.nordicsemi.android.service.DisconnectAndStopEvent import no.nordicsemi.android.service.ServiceManager +import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.data.ConfigurationDataSource import no.nordicsemi.android.uart.data.MacroEol import no.nordicsemi.android.uart.data.UARTMacro @@ -51,21 +53,18 @@ import no.nordicsemi.android.uart.data.UARTRecord import no.nordicsemi.android.uart.data.UARTRecordType import no.nordicsemi.android.uart.data.UARTServiceData import no.nordicsemi.android.uart.data.parseWithNewLineChar -import no.nordicsemi.android.ui.view.NordicLoggerFactory -import no.nordicsemi.android.ui.view.StringConst +import no.nordicsemi.android.utils.simpleSharedFlow +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton class UARTRepository @Inject internal constructor( - @ApplicationContext - private val context: Context, + @ApplicationContext private val context: Context, private val serviceManager: ServiceManager, private val configurationDataSource: ConfigurationDataSource, - private val stringConst: StringConst, - private val loggerFactory: NordicLoggerFactory ) { - private var logger: BleLoggerAndLauncher? = null + private var tree: nRFLoggerTree? = null private val _data = MutableStateFlow(UARTServiceData()) internal val data = _data.asStateFlow() @@ -98,9 +97,12 @@ class UARTRepository @Inject internal constructor( private fun shouldClean() = !isOnScreen && !isServiceRunning fun launch(device: ServerDevice) { - logger = loggerFactory.createNordicLogger(context, stringConst.APP_NAME, "UART", device.address) + tree = nRFLoggerTree(context, "UART", device.address, device.name) + .apply { setLoggingTagsEnabled(false) } + .also { Timber.plant(it) } + _data.value = _data.value.copy(deviceName = device.name) - serviceManager.startService(UARTService::class.java, device) + serviceManager.startService(UARTService::class.java, device, context.getString(R.string.uart_title)) } fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) { @@ -135,11 +137,7 @@ class UARTRepository @Inject internal constructor( } fun openLogger() { - logger?.launch() - } - - fun log(priority: Int, message: String) { - logger?.log(priority, message) + LoggerLauncher.launch(context, tree?.session as? LogSession) } fun onMissingServices() { @@ -156,7 +154,8 @@ class UARTRepository @Inject internal constructor( } private fun clean() { - logger = null + tree?.let { Timber.uproot(it) } + tree = null _data.value = UARTServiceData() } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt index ebcac9b57..cfec9e82c 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/repository/UARTService.kt @@ -43,7 +43,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattServices @@ -53,10 +52,11 @@ import no.nordicsemi.android.kotlin.ble.core.data.BleGattProperty import no.nordicsemi.android.kotlin.ble.core.data.BleWriteType import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.kotlin.ble.core.data.Mtu +import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService -import java.util.* +import java.util.UUID import javax.inject.Inject val UART_SERVICE_UUID: UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E") @@ -92,7 +92,7 @@ internal class UARTService : NotificationService() { } private fun startGattClient(device: ServerDevice) = lifecycleScope.launch { - val client = ClientBleGatt.connect(this@UARTService, device, lifecycleScope, logger = { p, s -> repository.log(p, s) }) + val client = ClientBleGatt.connect(this@UARTService, device, lifecycleScope) this@UARTService.client = client if (!client.isConnected) { @@ -128,14 +128,14 @@ internal class UARTService : NotificationService() { txCharacteristic.getNotifications() .map { String(it.value) } .onEach { repository.onNewMessageReceived(it) } - .onEach { repository.log(10, "Received: $it") } +// .onEach { repository.log(10, "Received: $it") } .catch { it.printStackTrace() } .launchIn(lifecycleScope) repository.command .onEach { rxCharacteristic.splitWrite(DataByteArray.from(it), getWriteType(rxCharacteristic)) } .onEach { repository.onNewMessageSent(it) } - .onEach { repository.log(10, "Sent: $it") } +// .onEach { repository.log(10, "Sent: $it") } .catch { it.printStackTrace() } .launchIn(lifecycleScope) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/InputSection.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/InputSection.kt index 597cca6ce..272a94ccc 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/InputSection.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/InputSection.kt @@ -45,21 +45,20 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch -import no.nordicsemi.android.common.theme.view.RadioButtonGroup -import no.nordicsemi.android.common.theme.view.RadioButtonItem -import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity +import no.nordicsemi.android.common.ui.view.RadioButtonGroup +import no.nordicsemi.android.common.ui.view.RadioButtonItem +import no.nordicsemi.android.common.ui.view.RadioGroupViewEntity import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.data.MacroEol import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle import no.nordicsemi.android.utils.EMPTY -@OptIn(ExperimentalMaterial3Api::class) @Composable internal fun InputSection(onEvent: (UARTViewEvent) -> Unit) { val text = rememberSaveable { mutableStateOf(String.EMPTY) } val hint = stringResource(id = R.string.uart_input_hint) - val checkedItem = rememberSaveable { mutableStateOf(MacroEol.values()[0]) } + val checkedItem = rememberSaveable { mutableStateOf(MacroEol.entries[0]) } Row(verticalAlignment = Alignment.CenterVertically) { Box(modifier = Modifier.weight(1f)) { @@ -99,9 +98,9 @@ internal fun InputSection(onEvent: (UARTViewEvent) -> Unit) { @Composable internal fun EditInputSection(onEvent: (UARTViewEvent) -> Unit) { - val checkedItem = rememberSaveable { mutableStateOf(MacroEol.values()[0]) } + val checkedItem = rememberSaveable { mutableStateOf(MacroEol.entries[0]) } - val items = MacroEol.values().map { + val items = MacroEol.entries.map { RadioButtonItem(it.toDisplayString(), it == checkedItem.value) } val viewEntity = RadioGroupViewEntity(items) @@ -131,7 +130,7 @@ internal fun EditInputSection(onEvent: (UARTViewEvent) -> Unit) { RadioButtonGroup(viewEntity) { val i = items.indexOf(it) - checkedItem.value = MacroEol.values()[i] + checkedItem.value = MacroEol.entries.toTypedArray()[i] } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt index d90ce04df..0300f1a55 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTAddMacroDialog.kt @@ -52,9 +52,9 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import no.nordicsemi.android.common.theme.view.RadioButtonGroup -import no.nordicsemi.android.common.theme.view.RadioButtonItem -import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity +import no.nordicsemi.android.common.ui.view.RadioButtonGroup +import no.nordicsemi.android.common.ui.view.RadioButtonItem +import no.nordicsemi.android.common.ui.view.RadioGroupViewEntity import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.data.MacroEol import no.nordicsemi.android.uart.data.MacroIcon @@ -67,7 +67,7 @@ private const val GRID_SIZE = 5 internal fun UARTAddMacroDialog(macro: UARTMacro?, onEvent: (UARTViewEvent) -> Unit) { val newLineChar = rememberSaveable { mutableStateOf(macro?.newLineChar ?: MacroEol.LF) } val command = rememberSaveable { mutableStateOf(macro?.command ?: String.EMPTY) } - val selectedIcon = rememberSaveable { mutableStateOf(macro?.icon ?: MacroIcon.values()[0]) } + val selectedIcon = rememberSaveable { mutableStateOf(macro?.icon ?: MacroIcon.entries.toTypedArray()[0]) } AlertDialog( onDismissRequest = { onEvent(OnEditFinish) }, @@ -130,7 +130,6 @@ internal fun UARTAddMacroDialog(macro: UARTMacro?, onEvent: (UARTViewEvent) -> U ) } -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun CommandInput(command: MutableState) { Column { @@ -150,7 +149,7 @@ private fun CommandInput(command: MutableState) { @Composable private fun NewLineCharSection(checkedItem: MacroEol, onItemClick: (MacroEol) -> Unit) { - val items = MacroEol.values().map { + val items = MacroEol.entries.map { RadioButtonItem(it.toDisplayString(), it == checkedItem) } val viewEntity = RadioGroupViewEntity(items) @@ -163,7 +162,7 @@ private fun NewLineCharSection(checkedItem: MacroEol, onItemClick: (MacroEol) -> RadioButtonGroup(viewEntity) { val i = items.indexOf(it) - onItemClick(MacroEol.values()[i]) + onItemClick(MacroEol.entries[i]) } } } diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt index 76ab058fe..cdbefacb4 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/view/UARTScreen.kt @@ -31,12 +31,10 @@ package no.nordicsemi.android.uart.view -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -46,16 +44,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import no.nordicsemi.android.common.theme.view.PagerView -import no.nordicsemi.android.common.theme.view.PagerViewEntity -import no.nordicsemi.android.common.theme.view.PagerViewItem +import no.nordicsemi.android.common.ui.view.PagerView +import no.nordicsemi.android.common.ui.view.PagerViewEntity +import no.nordicsemi.android.common.ui.view.PagerViewItem import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView +import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.uart.R import no.nordicsemi.android.uart.viewmodel.UARTViewModel import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.ProfileAppBar -import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceConnectingView -import no.nordicsemi.android.kotlin.ble.ui.scanner.view.DeviceDisconnectedView @Composable fun UARTScreen() { @@ -99,7 +97,6 @@ private fun PaddingBox(content: @Composable () -> Unit) { } } -@OptIn(ExperimentalFoundationApi::class) @Composable private fun SuccessScreen() { val input = stringResource(id = R.string.uart_input) diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index c528ef9d5..d1de07396 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -77,7 +77,6 @@ import no.nordicsemi.android.uart.view.OnRunMacro import no.nordicsemi.android.uart.view.OpenLogger import no.nordicsemi.android.uart.view.UARTViewEvent import no.nordicsemi.android.uart.view.UARTViewState -import no.nordicsemi.android.ui.view.NordicLoggerFactory import javax.inject.Inject @HiltViewModel @@ -86,9 +85,7 @@ internal class UARTViewModel @Inject constructor( private val navigationManager: Navigator, private val dataSource: UARTPersistentDataSource, private val analytics: AppAnalytics, - private val loggerFactory: NordicLoggerFactory ) : ViewModel() { - private val _state = MutableStateFlow(UARTViewState()) val state = _state.asStateFlow() diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt deleted file mode 100644 index 8799220fa..000000000 --- a/profile_uart/src/test/java/no/nordicsemi/android/gls/NordicLoggerFactoryTestModule.kt +++ /dev/null @@ -1,40 +0,0 @@ -package no.nordicsemi.android.gls - -import android.content.Context -import dagger.Module -import dagger.Provides -import dagger.hilt.components.SingletonComponent -import dagger.hilt.testing.TestInstallIn -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.ui.view.NordicLoggerFactory -import no.nordicsemi.android.ui.view.NordicLoggerFactoryHiltModule - -@Module -@TestInstallIn( - components = [SingletonComponent::class], - replaces = [NordicLoggerFactoryHiltModule::class] -) -class NordicLoggerFactoryTestModule { - - @Provides - fun createLogger(): NordicLoggerFactory { - return object : NordicLoggerFactory { - override fun createNordicLogger( - context: Context, - profile: String?, - key: String, - name: String?, - ): BleLoggerAndLauncher { - return object : BleLoggerAndLauncher { - override fun launch() { - - } - - override fun log(priority: Int, log: String) { - println(log) - } - } - } - } - } -} diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/ServiceManagerTestModule.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/ServiceManagerTestModule.kt index 70b59201e..142f63870 100644 --- a/profile_uart/src/test/java/no/nordicsemi/android/gls/ServiceManagerTestModule.kt +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/ServiceManagerTestModule.kt @@ -1,6 +1,5 @@ package no.nordicsemi.android.gls -import android.content.ComponentName import android.content.Context import android.content.Intent import dagger.Module @@ -25,8 +24,6 @@ import javax.inject.Singleton ) class ServiceManagerTestModule { - private val componentName = ComponentName("org.robolectric", UARTService::class.java.name) - @Provides internal fun provideDevice(): MockServerDevice { return MockServerDevice( @@ -49,7 +46,7 @@ class ServiceManagerTestModule { @Singleton internal fun provideServiceManager(controller: ServiceController): ServiceManager { return object : ServiceManager { - override fun startService(service: Class, device: ServerDevice) { + override fun startService(service: Class, device: ServerDevice, profile: String) { controller.create().startCommand(3, 4).get() } } diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/TestHiltModule.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/TestHiltModule.kt deleted file mode 100644 index 36e316cb0..000000000 --- a/profile_uart/src/test/java/no/nordicsemi/android/gls/TestHiltModule.kt +++ /dev/null @@ -1,13 +0,0 @@ -package no.nordicsemi.android.gls - -import dagger.Module -import dagger.hilt.components.SingletonComponent -import dagger.hilt.testing.TestInstallIn - -//@Module -//@TestInstallIn( -// components = [SingletonComponent::class], -// replaces = [AnalyticsModule::class] -//) -//class TestHiltModule { -//} diff --git a/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt b/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt index c6e368008..989d013e0 100644 --- a/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt +++ b/profile_uart/src/test/java/no/nordicsemi/android/gls/UARTViewModelTest.kt @@ -1,18 +1,13 @@ package no.nordicsemi.android.gls -import android.content.Context import androidx.test.rule.ServiceTestRule import dagger.hilt.android.testing.BindValue import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.HiltTestApplication import dagger.hilt.android.testing.UninstallModules -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.RelaxedMockK import io.mockk.junit4.MockKRule import io.mockk.mockk -import io.mockk.mockkObject import io.mockk.mockkStatic import io.mockk.spyk import kotlinx.coroutines.CoroutineScope @@ -23,10 +18,6 @@ import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain -import no.nordicsemi.android.analytics.AppAnalytics -import no.nordicsemi.android.common.core.ApplicationScope -import no.nordicsemi.android.common.logger.BleLoggerAndLauncher -import no.nordicsemi.android.common.logger.DefaultBleLogger import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.di.NavigationModule @@ -37,10 +28,7 @@ import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionStateWithStatus import no.nordicsemi.android.uart.UartServer import no.nordicsemi.android.uart.data.UARTPersistentDataSource import no.nordicsemi.android.uart.repository.UARTRepository -import no.nordicsemi.android.uart.view.DisconnectEvent import no.nordicsemi.android.uart.viewmodel.UARTViewModel -import no.nordicsemi.android.ui.view.NordicLoggerFactory -import no.nordicsemi.android.ui.view.StringConst import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before @@ -75,18 +63,6 @@ internal class UARTViewModelTest { @JvmField val analyticsService: Navigator = mockk(relaxed = true) - @RelaxedMockK - lateinit var analytics: AppAnalytics - - @MockK - lateinit var stringConst: StringConst - - @RelaxedMockK - lateinit var context: Context - - @RelaxedMockK - lateinit var logger: BleLoggerAndLauncher - @Inject lateinit var repository: UARTRepository @@ -113,34 +89,15 @@ internal class UARTViewModelTest { @Before fun before() { - viewModel = UARTViewModel(repository, mockk(relaxed = true), dataSource, mockk(relaxed = true), object : - NordicLoggerFactory { - override fun createNordicLogger( - context: Context, - profile: String?, - key: String, - name: String?, - ): BleLoggerAndLauncher { - return logger - } - - }) + viewModel = UARTViewModel(repository, mockk(relaxed = true), dataSource, mockk(relaxed = true)) runBlocking { mockkStatic("no.nordicsemi.android.common.core.ApplicationScopeKt") - every { ApplicationScope } returns CoroutineScope(UnconfinedTestDispatcher()) - every { stringConst.APP_NAME } returns "Test" uartServer = UartServer(CoroutineScope(UnconfinedTestDispatcher())) uartServer.start(spyk(), device) } } - @Before - fun prepareLogger() { - mockkObject(DefaultBleLogger.Companion) - every { DefaultBleLogger.create(any(), any(), any(), any()) } returns mockk() - } - @Test fun `when connected should return state connected`() = runTest { val connectedState = GattConnectionStateWithStatus( diff --git a/settings.gradle.kts b/settings.gradle.kts index 8b6358bd7..76db64550 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,7 +50,7 @@ dependencyResolutionManagement { } versionCatalogs { create("libs") { - from("no.nordicsemi.android.gradle:version-catalog:1.11.0") + from("no.nordicsemi.android.gradle:version-catalog:2.4") } } } @@ -79,6 +79,6 @@ include(":lib_utils") // includeBuild("../Android-Common-Libraries") //} // -if (file("../Kotlin-BLE-Library").exists()) { - includeBuild("../Kotlin-BLE-Library") -} +//if (file("../Kotlin-BLE-Library").exists()) { +// includeBuild("../Kotlin-BLE-Library") +//}