Migration to AGP and Commons Library (#142)

* Update dependencies

* Migrated logger feature

* Updated to be compatible with common library version 2.0.0

* Migration to Kotlin BLE 1.1.0

* Changed to Enum entries

* Removed unnecessary ExperimentalMaterial3Api annotation

* Migrated from SmallTopAppBar to TopAppBar

* Changed to data object

* Made nullable logger

* Changed to nullable

* Changed import
This commit is contained in:
Himali Aryal
2025-07-25 16:13:50 +02:00
committed by GitHub
parent a253906cf9
commit 9a71e66c10
56 changed files with 316 additions and 242 deletions

View File

@@ -66,7 +66,7 @@ dependencies {
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)
@@ -79,4 +79,8 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
}

View File

@@ -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
@@ -58,5 +59,7 @@ class NrfToolboxApplication : Application() {
uartServer.start(this)
glsServer.start(this)
Timber.plant(Timber.DebugTree())
}
}

View File

@@ -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)
}

View File

@@ -61,7 +61,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import no.nordicsemi.android.nrftoolbox.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FeatureButton(
@DrawableRes iconId: Int,

View File

@@ -34,7 +34,6 @@ package no.nordicsemi.android.nrftoolbox.view
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
@@ -69,7 +68,6 @@ private const val DFU_LINK = "https://play.google.com/store/apps/details?id=no.n
private const val LOGGER_PACKAGE_NAME = "no.nordicsemi.android.log"
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen() {
val viewModel: HomeViewModel = hiltViewModel()

View File

@@ -33,8 +33,8 @@ 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.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.colorResource
@@ -44,9 +44,9 @@ import no.nordicsemi.android.nrftoolbox.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TitleAppBar(text: String) {
SmallTopAppBar(
TopAppBar(
title = { Text(text, maxLines = 2) },
colors = TopAppBarDefaults.smallTopAppBarColors(
colors = TopAppBarDefaults.topAppBarColors(
scrolledContainerColor = MaterialTheme.colorScheme.primary,
containerColor = colorResource(id = R.color.appBarColor),
titleContentColor = MaterialTheme.colorScheme.onPrimary,

View File

@@ -114,7 +114,7 @@ class HomeViewModel @Inject constructor(
}
fun openLogger() {
LoggerLauncher.launch(context)
LoggerLauncher.launch(context, null)
}
fun logEvent(event: ProfileOpenEvent) {

View File

@@ -47,5 +47,7 @@ class NrfToolboxApplication : Application() {
super.onCreate()
analytics.logEvent(AppOpenEvent)
Timber.plant(Timber.DebugTree())
}
}

View File

@@ -32,20 +32,25 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.parcelize) apply false
alias(libs.plugins.kotlin.serialization) 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.hilt) apply false
alias(libs.plugins.secrets) apply false
alias(libs.plugins.protobuf) apply false
alias(libs.plugins.ksp) apply false
alias(libs.plugins.google.services) apply false
alias(libs.plugins.firebase.crashlytics) apply false
// Nordic plugins are defined in https://github.com/NordicSemiconductor/Android-Gradle-Plugins
alias(libs.plugins.nordic.application) apply false
alias(libs.plugins.nordic.application.compose) apply false
alias(libs.plugins.nordic.library) apply false
alias(libs.plugins.nordic.library.compose) apply false
alias(libs.plugins.nordic.hilt) apply false
alias(libs.plugins.nordic.feature) apply false
alias(libs.plugins.google.services) apply false
alias(libs.plugins.firebase.crashlytics) apply false
}

View File

@@ -50,3 +50,5 @@ android.useAndroidX=true
kotlin.code.style=official
android.nonTransitiveRClass=false
# https://github.com/google/ksp/issues/1942#issuecomment-2157733096
ksp.useKSP2=true

View File

@@ -31,7 +31,7 @@
#Mon Feb 14 14:46:55 CET 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@@ -35,7 +35,7 @@ import android.os.Bundle
sealed class FirebaseEvent(val eventName: String, val params: Bundle?)
object AppOpenEvent : FirebaseEvent("APP_OPEN", null)
data object AppOpenEvent : FirebaseEvent("APP_OPEN", null)
class ProfileOpenEvent : FirebaseEvent {

View File

@@ -42,7 +42,6 @@ android {
}
dependencies {
implementation(libs.nordic.uilogger)
implementation(libs.nordic.theme)
implementation(libs.nordic.logger)
@@ -52,4 +51,8 @@ dependencies {
implementation(libs.androidx.compose.material.iconsExtended)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.activity.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
}

View File

@@ -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
}

View File

@@ -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)
}
}
}
}

View File

@@ -36,11 +36,18 @@ 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.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
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
@@ -51,11 +58,11 @@ import no.nordicsemi.android.ui.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CloseIconAppBar(text: String, onClick: () -> Unit) {
SmallTopAppBar(
TopAppBar(
title = { Text(text, maxLines = 2) },
colors = TopAppBarDefaults.smallTopAppBarColors(
colors = TopAppBarDefaults.topAppBarColors(
scrolledContainerColor = MaterialTheme.colorScheme.primary,
containerColor = colorResource(id = R.color.appBarColor),
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
@@ -74,11 +81,11 @@ fun CloseIconAppBar(text: String, onClick: () -> Unit) {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoggerBackIconAppBar(text: String, onClick: () -> Unit) {
SmallTopAppBar(
TopAppBar(
title = { Text(text, maxLines = 2) },
colors = TopAppBarDefaults.smallTopAppBarColors(
colors = TopAppBarDefaults.topAppBarColors(
scrolledContainerColor = MaterialTheme.colorScheme.primary,
containerColor = colorResource(id = R.color.appBarColor),
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
@@ -108,11 +115,11 @@ fun LoggerBackIconAppBar(text: String, onClick: () -> Unit) {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BackIconAppBar(text: String, onClick: () -> Unit) {
SmallTopAppBar(
TopAppBar(
title = { Text(text, maxLines = 2) },
colors = TopAppBarDefaults.smallTopAppBarColors(
colors = TopAppBarDefaults.topAppBarColors(
scrolledContainerColor = MaterialTheme.colorScheme.primary,
containerColor = colorResource(id = R.color.appBarColor),
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
@@ -131,12 +138,17 @@ fun BackIconAppBar(text: String, onClick: () -> Unit) {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoggerIconAppBar(text: String, onClick: () -> Unit, onDisconnectClick: () -> Unit, onLoggerClick: () -> Unit) {
SmallTopAppBar(
fun LoggerIconAppBar(
text: String,
onClick: () -> Unit,
onDisconnectClick: () -> Unit,
onLoggerClick: () -> Unit
) {
TopAppBar(
title = { Text(text, maxLines = 2) },
colors = TopAppBarDefaults.smallTopAppBarColors(
colors = TopAppBarDefaults.topAppBarColors(
scrolledContainerColor = MaterialTheme.colorScheme.primary,
containerColor = colorResource(id = R.color.appBarColor),
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,

View File

@@ -51,7 +51,7 @@ dependencies {
implementation(libs.nordic.navigation)
implementation(libs.nordic.theme)
implementation(libs.nordic.uilogger)
implementation(libs.nordic.logger)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material.iconsExtended)
@@ -60,4 +60,8 @@ dependencies {
implementation(libs.androidx.lifecycle.service)
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
}

View File

@@ -53,8 +53,7 @@ import no.nordicsemi.android.bps.view.BPSViewEvent
import no.nordicsemi.android.bps.view.BPSViewState
import no.nordicsemi.android.bps.view.DisconnectEvent
import no.nordicsemi.android.bps.view.OpenLoggerEvent
import no.nordicsemi.android.common.logger.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 +66,11 @@ 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
@@ -93,7 +95,7 @@ internal class BPSViewModel @Inject constructor(
val state = _state.asStateFlow()
private var client: ClientBleGatt? = null
private lateinit var logger: BleLoggerAndLauncher
private var logger: nRFLoggerTree? = null
init {
navigationManager.navigateTo(ScannerDestinationId, ParcelUuid(BPS_SERVICE_UUID))
@@ -113,7 +115,7 @@ internal class BPSViewModel @Inject constructor(
fun onEvent(event: BPSViewEvent) {
when (event) {
DisconnectEvent -> onDisconnectEvent()
OpenLoggerEvent -> logger.launch()
OpenLoggerEvent -> LoggerLauncher.launch(context, logger?.session as? LogSession)
}
}
@@ -122,12 +124,18 @@ internal class BPSViewModel @Inject constructor(
navigationManager.navigateUp()
}
private fun initLogger(device: ServerDevice) {
logger?.let { Timber.uproot(it) }
logger = nRFLoggerTree(context, stringConst.APP_NAME, "BPS", device.address)
.also { Timber.plant(it) }
}
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)
initLogger(device)
val client = ClientBleGatt.connect(context, device, viewModelScope, logger = logger)
val client = ClientBleGatt.connect(context, device, viewModelScope)
this@BPSViewModel.client = client
client.connectionStateWithStatus

View File

@@ -47,7 +47,7 @@ dependencies {
implementation(libs.nordic.core)
implementation(libs.nordic.theme)
implementation(libs.nordic.uilogger)
implementation(libs.nordic.logger)
implementation(libs.nordic.navigation)
implementation(libs.nordic.blek.uiscanner)
@@ -61,4 +61,8 @@ dependencies {
implementation(libs.androidx.lifecycle.service)
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation(libs.slf4j.timber)
implementation(libs.nordic.log.timber)
}

View File

@@ -41,15 +41,17 @@ 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 timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -60,7 +62,7 @@ class CGMRepository @Inject constructor(
private val serviceManager: ServiceManager,
private val stringConst: StringConst
) {
private var logger: BleLoggerAndLauncher? = null
private var logger: nRFLoggerTree? = null
private val _data = MutableStateFlow(CGMServiceData())
internal val data = _data.asStateFlow()
@@ -92,8 +94,14 @@ class CGMRepository @Inject constructor(
private fun shouldClean() = !isOnScreen && !isServiceRunning
private fun initLogger(device: ServerDevice) {
logger?.let { Timber.uproot(it) }
logger = nRFLoggerTree(context, stringConst.APP_NAME, "CGM", device.address)
.also { Timber.plant(it) }
}
fun launch(device: ServerDevice) {
logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "CGM", device.address)
initLogger(device)
_data.value = _data.value.copy(deviceName = device.name)
serviceManager.startService(CGMService::class.java, device)
}
@@ -124,7 +132,7 @@ class CGMRepository @Inject constructor(
}
fun openLogger() {
logger?.launch()
LoggerLauncher.launch(context, logger?.session as? LogSession)
}
fun log(priority: Int, message: String) {

View File

@@ -132,7 +132,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

View File

@@ -46,8 +46,9 @@ dependencies {
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)
@@ -61,4 +62,8 @@ dependencies {
implementation(libs.androidx.lifecycle.service)
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
}

View File

@@ -38,8 +38,7 @@ import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import no.nordicsemi.android.common.core.simpleSharedFlow
import no.nordicsemi.android.common.logger.BleLoggerAndLauncher
import no.nordicsemi.android.common.logger.DefaultBleLogger
import no.nordicsemi.android.common.logger.LoggerLauncher
import no.nordicsemi.android.csc.data.CSCServiceData
import no.nordicsemi.android.csc.data.SpeedUnit
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
@@ -48,9 +47,12 @@ 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 timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -61,7 +63,7 @@ class CSCRepository @Inject constructor(
private val serviceManager: ServiceManager,
private val stringConst: StringConst
) {
private var logger: BleLoggerAndLauncher? = null
private var logger: nRFLoggerTree? = null
private val _wheelSize = MutableStateFlow(WheelSizes.default)
internal val wheelSize = _wheelSize.asStateFlow()
@@ -91,8 +93,14 @@ class CSCRepository @Inject constructor(
private fun shouldClean() = !isOnScreen && !isServiceRunning
private fun initLogger(device: ServerDevice) {
logger?.let { Timber.uproot(it) }
logger = nRFLoggerTree(context, stringConst.APP_NAME, "CSC", device.address)
.also { Timber.plant(it) }
}
fun launch(device: ServerDevice) {
logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "CSC", device.address)
initLogger(device)
_data.value = _data.value.copy(deviceName = device.name)
serviceManager.startService(CSCService::class.java, device)
}
@@ -123,7 +131,7 @@ class CSCRepository @Inject constructor(
}
fun openLogger() {
logger?.launch()
LoggerLauncher.launch(context, logger?.session as? LogSession)
}
fun log(priority: Int, message: String) {

View File

@@ -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

View File

@@ -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
@@ -107,8 +107,10 @@ private fun SettingsSection(
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
SectionTitle(icon = Icons.Default.Settings,
title = stringResource(R.string.csc_settings))
SectionTitle(
icon = Icons.Default.Settings,
title = stringResource(R.string.csc_settings)
)
Spacer(modifier = Modifier.height(16.dp))

View File

@@ -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.toTypedArray().map { createRadioButtonItem(it, this) }
)
}

View File

@@ -45,9 +45,10 @@ dependencies {
implementation(project(":lib_ui"))
implementation(project(":lib_utils"))
implementation(libs.nordic.core)
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)
@@ -65,6 +66,10 @@ dependencies {
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
testImplementation(libs.hilt.android.testing)
kaptTest(libs.hilt.compiler)
testImplementation(libs.androidx.test.rules)
@@ -73,7 +78,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)
}

View File

@@ -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)

View File

@@ -32,10 +32,9 @@
package no.nordicsemi.android.gls.main.viewmodel
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.os.ParcelUuid
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
@@ -50,7 +49,7 @@ import kotlinx.coroutines.launch
import no.nordicsemi.android.analytics.AppAnalytics
import no.nordicsemi.android.analytics.Profile
import no.nordicsemi.android.analytics.ProfileConnectedEvent
import no.nordicsemi.android.common.logger.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,11 +79,13 @@ 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 java.util.*
import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
val GLS_SERVICE_UUID: UUID = UUID.fromString("00001808-0000-1000-8000-00805f9b34fb")
@@ -100,15 +101,14 @@ val BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000
@SuppressLint("MissingPermission")
@HiltViewModel
internal class GLSViewModel @Inject constructor(
@ApplicationContext context: Context,
@ApplicationContext private val context: Context,
private val navigationManager: Navigator,
private val analytics: AppAnalytics,
private val stringConst: StringConst,
private val loggerFactory: NordicLoggerFactory
) : AndroidViewModel(context as Application) {
) : ViewModel() {
private var client: ClientBleGatt? = null
private lateinit var logger: BleLoggerAndLauncher
private var logger: nRFLoggerTree? = null
private lateinit var glucoseMeasurementCharacteristic: ClientBleGattCharacteristic
private lateinit var recordAccessControlPointCharacteristic: ClientBleGattCharacteristic
@@ -136,7 +136,7 @@ internal class GLSViewModel @Inject constructor(
fun onEvent(event: GLSScreenViewEvent) {
when (event) {
OpenLoggerEvent -> logger.launch()
OpenLoggerEvent -> LoggerLauncher.launch(context, logger?.session as? LogSession)
is OnWorkingModeSelected -> onEvent(event)
is OnGLSRecordClick -> navigateToDetails(event.record)
DisconnectEvent -> onDisconnectEvent()
@@ -167,10 +167,9 @@ internal class GLSViewModel @Inject constructor(
private fun startGattClient(device: ServerDevice) = viewModelScope.launch {
_state.value = _state.value.copy(deviceName = device.name)
initLogger(device)
logger = loggerFactory.createNordicLogger(getApplication(), stringConst.APP_NAME, "GLS", device.address)
val client = ClientBleGatt.connect(getApplication(), device, viewModelScope, logger = logger)
val client = ClientBleGatt.connect(context, device, viewModelScope)
this@GLSViewModel.client = client
client.waitForBonding()
@@ -333,4 +332,10 @@ internal class GLSViewModel @Inject constructor(
_state.value = _state.value.copyWithNewRequestStatus(RequestStatus.FAILED)
}
}
private fun initLogger(device: ServerDevice) {
logger?.let { Timber.uproot(it) }
logger = nRFLoggerTree(context, stringConst.APP_NAME, "GLS", device.address)
.also { Timber.plant(it) }
}
}

View File

@@ -48,7 +48,7 @@ dependencies {
implementation(libs.nordic.core)
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)
@@ -63,4 +63,8 @@ dependencies {
implementation(libs.androidx.lifecycle.service)
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
}

View File

@@ -38,16 +38,18 @@ 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.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 timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -58,7 +60,7 @@ class HRSRepository @Inject constructor(
private val serviceManager: ServiceManager,
private val stringConst: StringConst
) {
private var logger: BleLoggerAndLauncher? = null
private var logger: nRFLoggerTree? = null
private val _data = MutableStateFlow(HRSServiceData())
internal val data = _data.asStateFlow()
@@ -85,8 +87,14 @@ class HRSRepository @Inject constructor(
private fun shouldClean() = !isOnScreen && !isServiceRunning
private fun initLogger(device: ServerDevice) {
logger?.let { Timber.uproot(it) }
logger = nRFLoggerTree(context, stringConst.APP_NAME, "HRS", device.address)
.also { Timber.plant(it) }
}
fun launch(device: ServerDevice) {
logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "HRS", device.address)
initLogger(device)
_data.value = _data.value.copy(deviceName = device.name)
serviceManager.startService(HRSService::class.java, device)
}
@@ -117,7 +125,7 @@ class HRSRepository @Inject constructor(
}
fun openLogger() {
logger?.launch()
LoggerLauncher.launch(context, logger?.session as? LogSession)
}
fun log(priority: Int, message: String) {

View File

@@ -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()

View File

@@ -50,9 +50,10 @@ dependencies {
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)
@@ -61,4 +62,8 @@ dependencies {
implementation(libs.androidx.lifecycle.service)
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
}

View File

@@ -38,17 +38,19 @@ 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.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 timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -59,7 +61,7 @@ class HTSRepository @Inject constructor(
private val serviceManager: ServiceManager,
private val stringConst: StringConst
) {
private var logger: BleLoggerAndLauncher? = null
private var logger: nRFLoggerTree? = null
private val _data = MutableStateFlow(HTSServiceData())
internal val data = _data.asStateFlow()
@@ -86,9 +88,15 @@ class HTSRepository @Inject constructor(
private fun shouldClean() = !isOnScreen && !isServiceRunning
private fun initLogger(device: ServerDevice) {
logger?.let { Timber.uproot(it) }
logger = nRFLoggerTree(context, stringConst.APP_NAME, "HTS", device.address)
.also { Timber.plant(it) }
}
fun launch(device: ServerDevice) {
_data.value = _data.value.copy(deviceName = device.name)
logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "HTS", device.address)
initLogger(device)
serviceManager.startService(HTSService::class.java, device)
}
@@ -109,7 +117,7 @@ class HTSRepository @Inject constructor(
}
fun openLogger() {
logger?.launch()
LoggerLauncher.launch(context, logger?.session as? LogSession)
}
fun log(priority: Int, message: String) {

View File

@@ -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

View File

@@ -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

View File

@@ -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,11 +58,14 @@ internal fun String.toTemperatureUnit(): TemperatureUnit {
internal fun TemperatureUnit.temperatureSettingsItems(): RadioGroupViewEntity {
return RadioGroupViewEntity(
TemperatureUnit.values().map { createRadioButtonItem(it, this) }
TemperatureUnit.entries.map { createRadioButtonItem(it, this) }
)
}
private fun createRadioButtonItem(unit: TemperatureUnit, selectedTemperatureUnit: TemperatureUnit): RadioButtonItem {
private fun createRadioButtonItem(
unit: TemperatureUnit,
selectedTemperatureUnit: TemperatureUnit
): RadioButtonItem {
return RadioButtonItem(displayTemperature(unit), unit == selectedTemperatureUnit)
}

View File

@@ -53,7 +53,7 @@ dependencies {
implementation(libs.nordic.core)
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)
@@ -62,4 +62,8 @@ dependencies {
implementation(libs.androidx.lifecycle.service)
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
}

View File

@@ -38,16 +38,18 @@ 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.data.PRXServiceData
import no.nordicsemi.android.service.DisconnectAndStopEvent
import no.nordicsemi.android.service.ServiceManager
import no.nordicsemi.android.ui.view.StringConst
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -58,7 +60,7 @@ class PRXRepository @Inject internal constructor(
private val serviceManager: ServiceManager,
private val stringConst: StringConst
) {
private var logger: BleLoggerAndLauncher? = null
private var logger: nRFLoggerTree? = null
private val _data = MutableStateFlow(PRXServiceData())
internal val data = _data.asStateFlow()
@@ -88,8 +90,14 @@ class PRXRepository @Inject internal constructor(
private fun shouldClean() = !isOnScreen && !isServiceRunning
private fun initLogger(device: ServerDevice) {
logger?.let { Timber.uproot(it) }
logger = nRFLoggerTree(context, stringConst.APP_NAME, "PRX", device.address)
.also { Timber.plant(it) }
}
fun launch(device: ServerDevice) {
logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "PRX", device.address)
initLogger(device)
_data.value = _data.value.copy(deviceName = device.name)
serviceManager.startService(PRXService::class.java, device)
}
@@ -119,7 +127,7 @@ class PRXRepository @Inject internal constructor(
}
fun openLogger() {
logger?.launch()
LoggerLauncher.launch(context, logger?.session as? LogSession)
}
fun log(priority: Int, message: String) {

View File

@@ -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

View File

@@ -48,7 +48,7 @@ dependencies {
implementation(libs.nordic.core)
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)
@@ -61,4 +61,8 @@ dependencies {
implementation(libs.androidx.lifecycle.service)
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
}

View File

@@ -38,16 +38,18 @@ 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.data.RSCSServiceData
import no.nordicsemi.android.service.DisconnectAndStopEvent
import no.nordicsemi.android.service.ServiceManager
import no.nordicsemi.android.ui.view.StringConst
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -58,7 +60,7 @@ class RSCSRepository @Inject constructor(
private val serviceManager: ServiceManager,
private val stringConst: StringConst
) {
private var logger: BleLoggerAndLauncher? = null
private var logger: nRFLoggerTree? = null
private val _data = MutableStateFlow(RSCSServiceData())
internal val data = _data.asStateFlow()
@@ -85,8 +87,14 @@ class RSCSRepository @Inject constructor(
private fun shouldClean() = !isOnScreen && !isServiceRunning
private fun initLogger(device: ServerDevice) {
logger?.let { Timber.uproot(it) }
logger = nRFLoggerTree(context, stringConst.APP_NAME, "RSCS", device.address)
.also { Timber.plant(it) }
}
fun launch(device: ServerDevice) {
logger = DefaultBleLogger.create(context, stringConst.APP_NAME, "RSCS", device.address)
initLogger(device)
_data.value = _data.value.copy(deviceName = device.name)
serviceManager.startService(RSCSService::class.java, device)
}
@@ -109,7 +117,7 @@ class RSCSRepository @Inject constructor(
}
fun openLogger() {
logger?.launch()
LoggerLauncher.launch(context, logger?.session as? LogSession)
}
fun log(priority: Int, message: String) {

View File

@@ -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

View File

@@ -52,8 +52,9 @@ dependencies {
implementation(libs.nordic.core)
implementation(libs.nordic.theme)
implementation(libs.nordic.ui)
implementation(libs.nordic.navigation)
implementation(libs.nordic.uilogger)
implementation(libs.nordic.logger)
implementation(libs.nordic.blek.client)
implementation(libs.nordic.blek.profile)
@@ -66,12 +67,8 @@ dependencies {
implementation(libs.room.ktx)
ksp(libs.room.compiler)
implementation(libs.accompanist.pager)
implementation(libs.accompanist.pagerindicators)
implementation(libs.androidx.dataStore.core)
implementation(libs.androidx.dataStore.preferences)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material.iconsExtended)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.activity.compose)
@@ -79,6 +76,11 @@ dependencies {
implementation(libs.androidx.hilt.navigation.compose)
// Timber & SLF4J
implementation (libs.slf4j.timber)
implementation(libs.nordic.log.timber)
testImplementation(libs.hilt.android.testing)
kaptTest(libs.hilt.compiler)
testImplementation(libs.androidx.test.rules)
@@ -87,7 +89,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)

View File

@@ -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

View File

@@ -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")
}
}

View File

@@ -51,7 +51,7 @@ internal class CommentVisitor : Visitor {
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 - ")
for (icon in MacroIcon.entries) builder.append("\n - ")
.append(icon.toString())
element.comment = builder.toString()
}

View File

@@ -38,10 +38,12 @@ 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.data.ConfigurationDataSource
@@ -51,8 +53,8 @@ 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 timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -63,9 +65,8 @@ class UARTRepository @Inject internal constructor(
private val serviceManager: ServiceManager,
private val configurationDataSource: ConfigurationDataSource,
private val stringConst: StringConst,
private val loggerFactory: NordicLoggerFactory
) {
private var logger: BleLoggerAndLauncher? = null
private var logger: nRFLoggerTree? = null
private val _data = MutableStateFlow(UARTServiceData())
internal val data = _data.asStateFlow()
@@ -98,11 +99,17 @@ class UARTRepository @Inject internal constructor(
private fun shouldClean() = !isOnScreen && !isServiceRunning
fun launch(device: ServerDevice) {
logger = loggerFactory.createNordicLogger(context, stringConst.APP_NAME, "UART", device.address)
initLogger(device)
_data.value = _data.value.copy(deviceName = device.name)
serviceManager.startService(UARTService::class.java, device)
}
private fun initLogger(device : ServerDevice) {
logger?.let { Timber.uproot(it) }
logger = nRFLoggerTree(context, stringConst.APP_NAME, "UART", device.name ?: "Unknown")
.also { Timber.plant(it) }
}
fun onConnectionStateChanged(connectionState: GattConnectionStateWithStatus?) {
_data.value = _data.value.copy(connectionState = connectionState)
}
@@ -135,7 +142,7 @@ class UARTRepository @Inject internal constructor(
}
fun openLogger() {
logger?.launch()
LoggerLauncher.launch(context, logger?.session as? LogSession)
}
fun log(priority: Int, message: String) {

View File

@@ -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) {

View File

@@ -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)

View File

@@ -70,7 +70,6 @@ internal fun UARTAddConfigurationDialog(onEvent: (UARTViewEvent) -> Unit, onDism
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun NameInput(
name: MutableState<String>,

View File

@@ -34,12 +34,21 @@ package no.nordicsemi.android.uart.view
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
@@ -52,9 +61,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 +76,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 +139,6 @@ internal fun UARTAddMacroDialog(macro: UARTMacro?, onEvent: (UARTViewEvent) -> U
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CommandInput(command: MutableState<String>) {
Column {
@@ -150,7 +158,7 @@ private fun CommandInput(command: MutableState<String>) {
@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)

View File

@@ -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)

View File

@@ -39,20 +39,20 @@ internal sealed class UARTViewEvent
internal data class OnEditMacro(val position: Int) : UARTViewEvent()
internal data class OnCreateMacro(val macro: UARTMacro) : UARTViewEvent()
internal object OnDeleteMacro : UARTViewEvent()
internal object OnEditFinish : UARTViewEvent()
internal data object OnDeleteMacro : UARTViewEvent()
internal data object OnEditFinish : UARTViewEvent()
internal data class OnConfigurationSelected(val configuration: UARTConfiguration) : UARTViewEvent()
internal data class OnAddConfiguration(val name: String) : UARTViewEvent()
internal object OnEditConfiguration : UARTViewEvent()
internal object OnDeleteConfiguration : UARTViewEvent()
internal data object OnEditConfiguration : UARTViewEvent()
internal data object OnDeleteConfiguration : UARTViewEvent()
internal data class OnRunMacro(val macro: UARTMacro) : UARTViewEvent()
internal data class OnRunInput(val text: String, val newLineChar: MacroEol) : UARTViewEvent()
internal object ClearOutputItems : UARTViewEvent()
internal object DisconnectEvent : UARTViewEvent()
internal data object ClearOutputItems : UARTViewEvent()
internal data object DisconnectEvent : UARTViewEvent()
internal object NavigateUp : UARTViewEvent()
internal object OpenLogger : UARTViewEvent()
internal data object NavigateUp : UARTViewEvent()
internal data object OpenLogger : UARTViewEvent()
internal object MacroInputSwitchClick : UARTViewEvent()
internal data object MacroInputSwitchClick : UARTViewEvent()

View File

@@ -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,7 +85,6 @@ 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())

View File

@@ -50,7 +50,7 @@ dependencyResolutionManagement {
}
versionCatalogs {
create("libs") {
from("no.nordicsemi.android.gradle:version-catalog:1.11.1")
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")
//}