Migrate CSC profile to new library

This commit is contained in:
Sylwester Zielinski
2023-02-22 15:27:10 +01:00
parent 1857a60f7a
commit 65c5e483d5
57 changed files with 273 additions and 503 deletions

View File

@@ -8,9 +8,9 @@ import no.nordicsemi.android.common.navigation.viewmodel.SimpleNavigationViewMod
import no.nordicsemi.android.common.ui.scanner.DeviceSelected import no.nordicsemi.android.common.ui.scanner.DeviceSelected
import no.nordicsemi.android.common.ui.scanner.ScannerScreen import no.nordicsemi.android.common.ui.scanner.ScannerScreen
import no.nordicsemi.android.common.ui.scanner.ScanningCancelled import no.nordicsemi.android.common.ui.scanner.ScanningCancelled
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
val ScannerDestinationId = createDestination<ParcelUuid, DiscoveredBluetoothDevice>("uiscanner-destination") val ScannerDestinationId = createDestination<ParcelUuid, ServerDevice>("uiscanner-destination")
val ScannerDestination = defineDestination(ScannerDestinationId) { val ScannerDestination = defineDestination(ScannerDestinationId) {
val navigationViewModel = hiltViewModel<SimpleNavigationViewModel>() val navigationViewModel = hiltViewModel<SimpleNavigationViewModel>()

View File

@@ -44,6 +44,8 @@ dependencies {
implementation(libs.nordic.ble.ktx) implementation(libs.nordic.ble.ktx)
implementation(libs.nordic.uiscanner) implementation(libs.nordic.uiscanner)
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation(libs.androidx.lifecycle.service) implementation(libs.androidx.lifecycle.service)
implementation(libs.androidx.localbroadcastmanager) implementation(libs.androidx.localbroadcastmanager)
implementation(libs.androidx.core) implementation(libs.androidx.core)

View File

@@ -35,7 +35,7 @@ import android.bluetooth.BluetoothDevice
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import javax.inject.Inject import javax.inject.Inject
const val DEVICE_DATA = "device-data" const val DEVICE_DATA = "device-data"
@@ -45,7 +45,7 @@ class ServiceManager @Inject constructor(
private val context: Context private val context: Context
) { ) {
fun <T> startService(service: Class<T>, device: DiscoveredBluetoothDevice) { fun <T> startService(service: Class<T>, device: ServerDevice) {
val intent = Intent(context, service).apply { val intent = Intent(context, service).apply {
putExtra(DEVICE_DATA, device) putExtra(DEVICE_DATA, device)
} }

View File

@@ -46,6 +46,7 @@ dependencies {
implementation(project(":lib_utils")) implementation(project(":lib_utils"))
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1") implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1")
implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.common)
implementation(libs.nordic.ble.ktx) implementation(libs.nordic.ble.ktx)

View File

@@ -41,12 +41,11 @@ import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.bps.data.BPSData import no.nordicsemi.android.bps.data.BPSData
import no.nordicsemi.android.bps.data.BPSManager import no.nordicsemi.android.bps.data.BPSManager
import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.common.logger.NordicLoggerFactory
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.BleManagerResult
import no.nordicsemi.android.ui.view.StringConst import no.nordicsemi.android.ui.view.StringConst
import javax.inject.Inject import javax.inject.Inject
@@ -61,7 +60,7 @@ internal class BPSRepository @Inject constructor(
private var logger: NordicLogger? = null private var logger: NordicLogger? = null
fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow<BleManagerResult<BPSData>> = callbackFlow { fun downloadData(scope: CoroutineScope, device: ServerDevice): Flow<BleManagerResult<BPSData>> = callbackFlow {
val createdLogger = loggerFactory.create(stringConst.APP_NAME, "BPS", device.address).also { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "BPS", device.address).also {
logger = it logger = it
} }
@@ -81,15 +80,15 @@ internal class BPSRepository @Inject constructor(
} }
} }
private suspend fun BPSManager.start(device: DiscoveredBluetoothDevice) { private suspend fun BPSManager.start(device: ServerDevice) {
try { // try {
connect(device.device) // connect(device.device)
.useAutoConnect(false) // .useAutoConnect(false)
.retry(3, 100) // .retry(3, 100)
.suspend() // .suspend()
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} // }
} }
fun openLogger() { fun openLogger() {

View File

@@ -34,7 +34,6 @@ package no.nordicsemi.android.bps.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable

View File

@@ -52,7 +52,7 @@ import no.nordicsemi.android.bps.view.OpenLoggerEvent
import no.nordicsemi.android.bps.view.WorkingState import no.nordicsemi.android.bps.view.WorkingState
import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.service.ConnectedResult
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
import javax.inject.Inject import javax.inject.Inject
@@ -75,7 +75,7 @@ internal class BPSViewModel @Inject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun handleArgs(result: NavigationResult<DiscoveredBluetoothDevice>) { private fun handleArgs(result: NavigationResult<ServerDevice>) {
when (result) { when (result) {
is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Cancelled -> navigationManager.navigateUp()
is NavigationResult.Success -> connectDevice(result.value) is NavigationResult.Success -> connectDevice(result.value)
@@ -89,7 +89,7 @@ internal class BPSViewModel @Inject constructor(
} }
} }
private fun connectDevice(device: DiscoveredBluetoothDevice) { private fun connectDevice(device: ServerDevice) {
repository.downloadData(viewModelScope, device).onEach { repository.downloadData(viewModelScope, device).onEach {
_state.value = WorkingState(it) _state.value = WorkingState(it)

View File

@@ -45,6 +45,9 @@ dependencies {
implementation(project(":lib_ui")) implementation(project(":lib_ui"))
implementation(project(":lib_utils")) implementation(project(":lib_utils"))
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1")
implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.common)
implementation(libs.nordic.ble.ktx) implementation(libs.nordic.ble.ktx)
implementation(libs.nordic.uilogger) implementation(libs.nordic.uilogger)

View File

@@ -45,7 +45,7 @@ import no.nordicsemi.android.cgms.data.CGMData
import no.nordicsemi.android.cgms.data.CGMManager import no.nordicsemi.android.cgms.data.CGMManager
import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.common.logger.NordicLoggerFactory
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.BleManagerResult
import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.IdleResult
import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.service.ServiceManager
@@ -70,11 +70,11 @@ class CGMRepository @Inject constructor(
val isRunning = data.map { it.isRunning() } val isRunning = data.map { it.isRunning() }
val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } val hasBeenDisconnected = data.map { it.hasBeenDisconnected() }
fun launch(device: DiscoveredBluetoothDevice) { fun launch(device: ServerDevice) {
serviceManager.startService(CGMService::class.java, device) serviceManager.startService(CGMService::class.java, device)
} }
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { fun start(device: ServerDevice, scope: CoroutineScope) {
val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CGMS", device.address).also { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CGMS", device.address).also {
logger = it logger = it
} }
@@ -90,15 +90,15 @@ class CGMRepository @Inject constructor(
} }
} }
private suspend fun CGMManager.start(device: DiscoveredBluetoothDevice) { private suspend fun CGMManager.start(device: ServerDevice) {
try { // try {
connect(device.device) // connect(device.device)
.useAutoConnect(false) // .useAutoConnect(false)
.retry(3, 100) // .retry(3, 100)
.suspend() // .suspend()
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} // }
} }
fun requestAllRecords() { fun requestAllRecords() {

View File

@@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService import no.nordicsemi.android.service.NotificationService
import javax.inject.Inject import javax.inject.Inject
@@ -50,7 +50,7 @@ internal class CGMService : NotificationService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
val device = intent!!.getParcelableExtra<DiscoveredBluetoothDevice>(DEVICE_DATA)!! val device = intent!!.getParcelableExtra<ServerDevice>(DEVICE_DATA)!!
repository.start(device, lifecycleScope) repository.start(device, lifecycleScope)

View File

@@ -38,7 +38,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search

View File

@@ -57,7 +57,7 @@ import no.nordicsemi.android.cgms.view.OpenLoggerEvent
import no.nordicsemi.android.cgms.view.WorkingState import no.nordicsemi.android.cgms.view.WorkingState
import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.service.ConnectedResult
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
import javax.inject.Inject import javax.inject.Inject
@@ -105,7 +105,7 @@ internal class CGMViewModel @Inject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) { private fun handleResult(result: NavigationResult<ServerDevice>) {
when (result) { when (result) {
is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Cancelled -> navigationManager.navigateUp()
is NavigationResult.Success -> repository.launch(result.value) is NavigationResult.Success -> repository.launch(result.value)

View File

@@ -45,6 +45,9 @@ dependencies {
implementation(project(":lib_ui")) implementation(project(":lib_ui"))
implementation(project(":lib_utils")) implementation(project(":lib_utils"))
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1")
implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.common)
implementation(libs.nordic.ble.ktx) implementation(libs.nordic.ble.ktx)
implementation(libs.nordic.uilogger) implementation(libs.nordic.uilogger)

View File

@@ -1,43 +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.csc.data
internal data class CSCData(
val scanDevices: Boolean = false,
val speed: Float = 0f,
val cadence: Float = 0f,
val distance: Float = 0f,
val totalDistance: Float = 0f,
val gearRatio: Float = 0f,
val batteryLevel: Int? = null,
val wheelSize: WheelSize = WheelSize()
)

View File

@@ -1,143 +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.csc.data
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.content.Context
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.ble.BleManager
import no.nordicsemi.android.ble.common.callback.battery.BatteryLevelResponse
import no.nordicsemi.android.ble.common.callback.csc.CyclingSpeedAndCadenceMeasurementResponse
import no.nordicsemi.android.ble.ktx.asValidResponseFlow
import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.service.ConnectionObserverAdapter
import java.util.*
val CSC_SERVICE_UUID: UUID = UUID.fromString("00001816-0000-1000-8000-00805f9b34fb")
private val CSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A5B-0000-1000-8000-00805f9b34fb")
private val BATTERY_SERVICE_UUID = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb")
private val BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb")
internal class CSCManager(
context: Context,
private val scope: CoroutineScope,
private val logger: NordicLogger
) : BleManager(context) {
private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null
private var cscMeasurementCharacteristic: BluetoothGattCharacteristic? = null
private var wheelSize: WheelSize = WheelSize()
private var previousResponse: CyclingSpeedAndCadenceMeasurementResponse? = null
private val data = MutableStateFlow(CSCData())
val dataHolder = ConnectionObserverAdapter<CSCData>()
init {
connectionObserver = dataHolder
data.onEach {
dataHolder.setValue(it)
}.launchIn(scope)
}
override fun log(priority: Int, message: String) {
logger.log(priority, message)
}
override fun getMinLogPriority(): Int {
return Log.VERBOSE
}
override fun getGattCallback(): BleManagerGattCallback {
return CSCManagerGattCallback()
}
fun setWheelSize(value: WheelSize) {
wheelSize = value
}
private inner class CSCManagerGattCallback : BleManagerGattCallback() {
override fun initialize() {
super.initialize()
setNotificationCallback(cscMeasurementCharacteristic).asValidResponseFlow<CyclingSpeedAndCadenceMeasurementResponse>()
.onEach {
previousResponse?.let { previousResponse ->
val wheelCircumference = wheelSize.value.toFloat()
val totalDistance = it.getTotalDistance(wheelSize.value.toFloat())
val distance = it.getDistance(wheelCircumference, previousResponse)
val speed = it.getSpeed(wheelCircumference, previousResponse)
val crankCadence = it.getCrankCadence(previousResponse)
val gearRatio = it.getGearRatio(previousResponse)
data.tryEmit(data.value.copy(
totalDistance = totalDistance,
distance = distance,
speed = speed,
wheelSize = wheelSize,
cadence = crankCadence,
gearRatio = gearRatio,
))
}
previousResponse = it
}.launchIn(scope)
enableNotifications(cscMeasurementCharacteristic).enqueue()
setNotificationCallback(batteryLevelCharacteristic).asValidResponseFlow<BatteryLevelResponse>().onEach {
data.value = data.value.copy(batteryLevel = it.batteryLevel)
}.launchIn(scope)
enableNotifications(batteryLevelCharacteristic).enqueue()
}
public override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
gatt.getService(CSC_SERVICE_UUID)?.run {
cscMeasurementCharacteristic = getCharacteristic(CSC_MEASUREMENT_CHARACTERISTIC_UUID)
}
gatt.getService(BATTERY_SERVICE_UUID)?.run {
batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)
}
return cscMeasurementCharacteristic != null
}
override fun onServicesInvalidated() {
cscMeasurementCharacteristic = null
batteryLevelCharacteristic = null
}
}
}

View File

@@ -0,0 +1,10 @@
package no.nordicsemi.android.csc.data
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData
data class CSCServicesData(
val data: CSCData = CSCData(),
val batteryLevel: Int? = null,
val connectionState: GattConnectionState = GattConnectionState.STATE_DISCONNECTED
)

View File

@@ -1,39 +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.csc.data
import no.nordicsemi.android.csc.view.CSCSettings
data class WheelSize(
val value: Int = CSCSettings.DefaultWheelSize.VALUE,
val name: String = CSCSettings.DefaultWheelSize.NAME
)

View File

@@ -33,22 +33,17 @@ package no.nordicsemi.android.csc.repository
import android.content.Context import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.common.logger.NordicLoggerFactory
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.csc.data.CSCServicesData
import no.nordicsemi.android.csc.data.CSCData import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.csc.data.CSCManager import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
import no.nordicsemi.android.csc.data.WheelSize import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData
import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize
import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSizes
import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.service.ServiceManager
import no.nordicsemi.android.ui.view.StringConst import no.nordicsemi.android.ui.view.StringConst
import javax.inject.Inject import javax.inject.Inject
@@ -62,48 +57,36 @@ class CSCRepository @Inject constructor(
private val loggerFactory: NordicLoggerFactory, private val loggerFactory: NordicLoggerFactory,
private val stringConst: StringConst private val stringConst: StringConst
) { ) {
private var manager: CSCManager? = null
private var logger: NordicLogger? = null private var logger: NordicLogger? = null
private val _data = MutableStateFlow<BleManagerResult<CSCData>>(IdleResult()) private val _wheelSize = MutableStateFlow(WheelSizes.default)
internal val wheelSize = _wheelSize.asStateFlow()
private val _data = MutableStateFlow(CSCServicesData())
internal val data = _data.asStateFlow() internal val data = _data.asStateFlow()
val isRunning = data.map { it.isRunning() } val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED }
val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } val hasBeenDisconnected =
data.map { it.connectionState != GattConnectionState.STATE_CONNECTED && it.connectionState != GattConnectionState.STATE_CONNECTING }
fun launch(device: DiscoveredBluetoothDevice) { fun launch(device: ServerDevice) {
serviceManager.startService(CSCService::class.java, device) serviceManager.startService(CSCService::class.java, device)
} }
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) {
val createdLogger = loggerFactory.create(stringConst.APP_NAME, "CSC", device.address).also {
logger = it
}
val manager = CSCManager(context, scope, createdLogger)
this.manager = manager
manager.dataHolder.status.onEach {
_data.value = it
}.launchIn(scope)
scope.launch {
manager.start(device)
}
}
fun setWheelSize(wheelSize: WheelSize) { fun setWheelSize(wheelSize: WheelSize) {
manager?.setWheelSize(wheelSize) _wheelSize.value = wheelSize
} }
private suspend fun CSCManager.start(device: DiscoveredBluetoothDevice) { fun onConnectionStateChanged(connectionState: GattConnectionState) {
try { _data.value = _data.value.copy(connectionState = connectionState)
connect(device.device) }
.useAutoConnect(false)
.retry(3, 100) fun onBatteryLevelChanged(batteryLevel: Int) {
.suspend() _data.value = _data.value.copy(batteryLevel = batteryLevel)
} catch (e: Exception) { }
e.printStackTrace()
} fun onCSCDataChanged(cscData: CSCData) {
_data.value = _data.value.copy(data = cscData)
} }
fun openLogger() { fun openLogger() {
@@ -111,8 +94,7 @@ class CSCRepository @Inject constructor(
} }
fun release() { fun release() {
manager?.disconnect()?.enqueue()
logger = null logger = null
manager = null serviceManager.stopService(CSCService::class.java)
} }
} }

View File

@@ -31,16 +31,32 @@
package no.nordicsemi.android.csc.repository package no.nordicsemi.android.csc.repository
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import kotlinx.coroutines.launch
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices
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.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService import no.nordicsemi.android.service.NotificationService
import java.util.*
import javax.inject.Inject import javax.inject.Inject
val CSC_SERVICE_UUID: UUID = UUID.fromString("00001816-0000-1000-8000-00805f9b34fb")
private val CSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A5B-0000-1000-8000-00805f9b34fb")
private val BATTERY_SERVICE_UUID = UUID.fromString("0000180F-0000-1000-8000-00805f9b34fb")
private val BATTERY_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A19-0000-1000-8000-00805f9b34fb")
@SuppressLint("MissingPermission")
@AndroidEntryPoint @AndroidEntryPoint
internal class CSCService : NotificationService() { internal class CSCService : NotificationService() {
@@ -50,14 +66,44 @@ internal class CSCService : NotificationService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
val device = intent!!.getParcelableExtra<DiscoveredBluetoothDevice>(DEVICE_DATA)!! val device = intent!!.getParcelableExtra<ServerDevice>(DEVICE_DATA)!!
repository.start(device, lifecycleScope) startGattClient(device)
repository.hasBeenDisconnected.onEach {
if (it) stopSelf()
}.launchIn(lifecycleScope)
return START_REDELIVER_INTENT return START_REDELIVER_INTENT
} }
private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch {
val client = blinkyDevice.connect(this@CSCService)
client.connection
.onEach { repository.onConnectionStateChanged(it.connectionState) }
.map { it.services }
.filterNotNull()
.onEach { configureGatt(it) }
.launchIn(lifecycleScope)
}
private suspend fun configureGatt(services: BleGattServices) {
val cscService = services.findService(CSC_SERVICE_UUID)!!
val cscMeasurementCharacteristic = cscService.findCharacteristic(CSC_MEASUREMENT_CHARACTERISTIC_UUID)!!
cscMeasurementCharacteristic.enableNotifications()
val cscDataParser = CSCDataParser()
cscMeasurementCharacteristic.notification
.mapNotNull { cscDataParser.parse(it, repository.wheelSize.value) }
.onEach { repository.onCSCDataChanged(it) }
.launchIn(lifecycleScope)
val batteryService = services.findService(BATTERY_SERVICE_UUID)!!
val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!!
// batteryLevelCharacteristic.enableNotifications()
batteryLevelCharacteristic.notification
.mapNotNull { BatteryLevelParser.parse(it) }
.onEach { repository.onBatteryLevelChanged(it) }
.launchIn(lifecycleScope)
}
} }

View File

@@ -34,7 +34,6 @@ package no.nordicsemi.android.csc.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Button import androidx.compose.material3.Button
@@ -50,15 +49,16 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import no.nordicsemi.android.common.theme.view.RadioButtonGroup import no.nordicsemi.android.common.theme.view.RadioButtonGroup
import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.R
import no.nordicsemi.android.csc.data.CSCData import no.nordicsemi.android.csc.data.CSCServicesData
import no.nordicsemi.android.csc.data.WheelSize import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData
import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize
import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.ScreenSection
import no.nordicsemi.android.ui.view.SectionTitle import no.nordicsemi.android.ui.view.SectionTitle
import no.nordicsemi.android.ui.view.dialog.FlowCanceled import no.nordicsemi.android.ui.view.dialog.FlowCanceled
import no.nordicsemi.android.ui.view.dialog.ItemSelectedResult import no.nordicsemi.android.ui.view.dialog.ItemSelectedResult
@Composable @Composable
internal fun CSCContentView(state: CSCData, speedUnit: SpeedUnit, onEvent: (CSCViewEvent) -> Unit) { internal fun CSCContentView(state: CSCServicesData, speedUnit: SpeedUnit, onEvent: (CSCViewEvent) -> Unit) {
val showDialog = rememberSaveable { mutableStateOf(false) } val showDialog = rememberSaveable { mutableStateOf(false) }
if (showDialog.value) { if (showDialog.value) {
@@ -69,8 +69,7 @@ internal fun CSCContentView(state: CSCData, speedUnit: SpeedUnit, onEvent: (CSCV
when (it) { when (it) {
FlowCanceled -> showDialog.value = false FlowCanceled -> showDialog.value = false
is ItemSelectedResult -> { is ItemSelectedResult -> {
onEvent(OnWheelSizeSelected(WheelSize(wheelValues[it.index].toInt(), onEvent(OnWheelSizeSelected(WheelSize(wheelValues[it.index].toInt(), wheelEntries[it.index])))
wheelEntries[it.index])))
showDialog.value = false showDialog.value = false
} }
} }
@@ -80,7 +79,7 @@ internal fun CSCContentView(state: CSCData, speedUnit: SpeedUnit, onEvent: (CSCV
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
SettingsSection(state, speedUnit, onEvent) { showDialog.value = true } SettingsSection(state.data, speedUnit, onEvent) { showDialog.value = true }
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
@@ -126,5 +125,5 @@ private fun SettingsSection(
@Preview @Preview
@Composable @Composable
private fun ConnectedPreview() { private fun ConnectedPreview() {
CSCContentView(CSCData(), SpeedUnit.KM_H) { } CSCContentView(CSCServicesData(), SpeedUnit.KM_H) { }
} }

View File

@@ -33,7 +33,7 @@ package no.nordicsemi.android.csc.view
import no.nordicsemi.android.common.theme.view.RadioButtonItem import no.nordicsemi.android.common.theme.view.RadioButtonItem
import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity import no.nordicsemi.android.common.theme.view.RadioGroupViewEntity
import no.nordicsemi.android.csc.data.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData
import java.util.* import java.util.*
private const val DISPLAY_M_S = "m/s" private const val DISPLAY_M_S = "m/s"

View File

@@ -48,15 +48,8 @@ import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView
import no.nordicsemi.android.common.ui.scanner.view.Reason import no.nordicsemi.android.common.ui.scanner.view.Reason
import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.R
import no.nordicsemi.android.csc.viewmodel.CSCViewModel import no.nordicsemi.android.csc.viewmodel.CSCViewModel
import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
import no.nordicsemi.android.service.ConnectingResult
import no.nordicsemi.android.service.DeviceHolder import no.nordicsemi.android.service.DeviceHolder
import no.nordicsemi.android.service.DisconnectedResult
import no.nordicsemi.android.service.IdleResult
import no.nordicsemi.android.service.LinkLossResult
import no.nordicsemi.android.service.MissingServiceResult
import no.nordicsemi.android.service.SuccessResult
import no.nordicsemi.android.service.UnknownErrorResult
import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.BackIconAppBar
import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar
import no.nordicsemi.android.ui.view.NavigateUpButton import no.nordicsemi.android.ui.view.NavigateUpButton
@@ -80,20 +73,11 @@ fun CSCScreen() {
) { ) {
when (state.cscManagerState) { when (state.cscManagerState) {
NoDeviceState -> DeviceConnectingView() NoDeviceState -> DeviceConnectingView()
is WorkingState -> when (state.cscManagerState.result) { is WorkingState -> when (state.cscManagerState.result.connectionState) {
is IdleResult, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) }
is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED,
is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } GattConnectionState.STATE_CONNECTED -> CSCContentView(state.cscManagerState.result, state.speedUnit) { viewModel.onEvent(it) }
is LinkLossResult -> DeviceDisconnectedView(Reason.LINK_LOSS) { NavigateUpButton(navigateUp) }
is MissingServiceResult -> DeviceDisconnectedView(Reason.MISSING_SERVICE) {
NavigateUpButton(navigateUp)
}
is UnknownErrorResult -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
is SuccessResult -> CSCContentView(
state.cscManagerState.result.data,
state.speedUnit
) { viewModel.onEvent(it) }
} }
} }
} }

View File

@@ -1,40 +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.csc.view
internal object CSCSettings {
object DefaultWheelSize {
const val NAME = "60-622"
const val VALUE = 2340
}
}

View File

@@ -31,8 +31,7 @@
package no.nordicsemi.android.csc.view package no.nordicsemi.android.csc.view
import no.nordicsemi.android.csc.data.CSCData import no.nordicsemi.android.csc.data.CSCServicesData
import no.nordicsemi.android.service.BleManagerResult
internal data class CSCViewState( internal data class CSCViewState(
val speedUnit: SpeedUnit = SpeedUnit.M_S, val speedUnit: SpeedUnit = SpeedUnit.M_S,
@@ -41,6 +40,6 @@ internal data class CSCViewState(
internal sealed class CSCMangerState internal sealed class CSCMangerState
internal data class WorkingState(val result: BleManagerResult<CSCData>) : CSCMangerState() internal data class WorkingState(val result: CSCServicesData) : CSCMangerState()
internal object NoDeviceState : CSCMangerState() internal object NoDeviceState : CSCMangerState()

View File

@@ -31,7 +31,7 @@
package no.nordicsemi.android.csc.view package no.nordicsemi.android.csc.view
import no.nordicsemi.android.csc.data.WheelSize import no.nordicsemi.android.kotlin.ble.profile.csc.WheelSize
internal sealed class CSCViewEvent internal sealed class CSCViewEvent

View File

@@ -40,32 +40,33 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.R
import no.nordicsemi.android.csc.data.CSCData import no.nordicsemi.android.csc.data.CSCServicesData
import no.nordicsemi.android.ui.view.BatteryLevelView import no.nordicsemi.android.ui.view.BatteryLevelView
import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.KeyValueField
import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.ScreenSection
import no.nordicsemi.android.ui.view.SectionTitle import no.nordicsemi.android.ui.view.SectionTitle
@Composable @Composable
internal fun SensorsReadingView(state: CSCData, speedUnit: SpeedUnit) { internal fun SensorsReadingView(state: CSCServicesData, speedUnit: SpeedUnit) {
val csc = state.data
ScreenSection { ScreenSection {
SectionTitle(resId = R.drawable.ic_records, title = "Records") SectionTitle(resId = R.drawable.ic_records, title = "Records")
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Column { Column {
KeyValueField(stringResource(id = R.string.csc_field_speed), state.displaySpeed(speedUnit)) KeyValueField(stringResource(id = R.string.csc_field_speed), csc.displaySpeed(speedUnit))
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
KeyValueField(stringResource(id = R.string.csc_field_cadence), state.displayCadence()) KeyValueField(stringResource(id = R.string.csc_field_cadence), csc.displayCadence())
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
KeyValueField(stringResource(id = R.string.csc_field_distance), state.displayDistance(speedUnit)) KeyValueField(stringResource(id = R.string.csc_field_distance), csc.displayDistance(speedUnit))
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
KeyValueField( KeyValueField(
stringResource(id = R.string.csc_field_total_distance), stringResource(id = R.string.csc_field_total_distance),
state.displayTotalDistance(speedUnit) csc.displayTotalDistance(speedUnit)
) )
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
KeyValueField(stringResource(id = R.string.csc_field_gear_ratio), state.displayGearRatio()) KeyValueField(stringResource(id = R.string.csc_field_gear_ratio), csc.displayGearRatio())
} }
} }
@@ -79,5 +80,5 @@ internal fun SensorsReadingView(state: CSCData, speedUnit: SpeedUnit) {
@Preview @Preview
@Composable @Composable
private fun Preview() { private fun Preview() {
SensorsReadingView(CSCData(), SpeedUnit.KM_H) SensorsReadingView(CSCServicesData(), SpeedUnit.KM_H)
} }

View File

@@ -47,7 +47,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import no.nordicsemi.android.csc.R import no.nordicsemi.android.csc.R
import no.nordicsemi.android.csc.data.CSCData import no.nordicsemi.android.kotlin.ble.profile.csc.CSCData
@Composable @Composable
internal fun WheelSizeView(state: CSCData, onClick: () -> Unit) { internal fun WheelSizeView(state: CSCData, onClick: () -> Unit) {

View File

@@ -46,9 +46,8 @@ import no.nordicsemi.android.analytics.Profile
import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.analytics.ProfileConnectedEvent
import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice
import no.nordicsemi.android.csc.data.CSC_SERVICE_UUID
import no.nordicsemi.android.csc.repository.CSCRepository import no.nordicsemi.android.csc.repository.CSCRepository
import no.nordicsemi.android.csc.repository.CSC_SERVICE_UUID
import no.nordicsemi.android.csc.view.CSCViewEvent import no.nordicsemi.android.csc.view.CSCViewEvent
import no.nordicsemi.android.csc.view.CSCViewState import no.nordicsemi.android.csc.view.CSCViewState
import no.nordicsemi.android.csc.view.NavigateUp import no.nordicsemi.android.csc.view.NavigateUp
@@ -58,6 +57,8 @@ import no.nordicsemi.android.csc.view.OnWheelSizeSelected
import no.nordicsemi.android.csc.view.OpenLogger import no.nordicsemi.android.csc.view.OpenLogger
import no.nordicsemi.android.csc.view.SpeedUnit import no.nordicsemi.android.csc.view.SpeedUnit
import no.nordicsemi.android.csc.view.WorkingState import no.nordicsemi.android.csc.view.WorkingState
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.service.ConnectedResult
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
import javax.inject.Inject import javax.inject.Inject
@@ -82,7 +83,7 @@ internal class CSCViewModel @Inject constructor(
repository.data.onEach { repository.data.onEach {
_state.value = _state.value.copy(cscManagerState = WorkingState(it)) _state.value = _state.value.copy(cscManagerState = WorkingState(it))
(it as? ConnectedResult)?.let { if (it.connectionState == GattConnectionState.STATE_CONNECTED) {
analytics.logEvent(ProfileConnectedEvent(Profile.CSC)) analytics.logEvent(ProfileConnectedEvent(Profile.CSC))
} }
}.launchIn(viewModelScope) }.launchIn(viewModelScope)
@@ -96,7 +97,7 @@ internal class CSCViewModel @Inject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) { private fun handleResult(result: NavigationResult<ServerDevice>) {
when (result) { when (result) {
is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Cancelled -> navigationManager.navigateUp()
is NavigationResult.Success -> repository.launch(result.value) is NavigationResult.Success -> repository.launch(result.value)

View File

@@ -45,6 +45,9 @@ dependencies {
implementation(project(":lib_ui")) implementation(project(":lib_ui"))
implementation(project(":lib_utils")) implementation(project(":lib_utils"))
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1")
implementation(libs.chart) implementation(libs.chart)
implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.common)

View File

@@ -32,7 +32,6 @@
package no.nordicsemi.android.gls.main.viewmodel package no.nordicsemi.android.gls.main.viewmodel
import android.os.ParcelUuid import android.os.ParcelUuid
import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@@ -45,7 +44,6 @@ import no.nordicsemi.android.analytics.Profile
import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.analytics.ProfileConnectedEvent
import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice
import no.nordicsemi.android.gls.GlsDetailsDestinationId import no.nordicsemi.android.gls.GlsDetailsDestinationId
import no.nordicsemi.android.gls.data.GLS_SERVICE_UUID import no.nordicsemi.android.gls.data.GLS_SERVICE_UUID
import no.nordicsemi.android.gls.main.view.DisconnectEvent import no.nordicsemi.android.gls.main.view.DisconnectEvent
@@ -57,6 +55,7 @@ import no.nordicsemi.android.gls.main.view.OnWorkingModeSelected
import no.nordicsemi.android.gls.main.view.OpenLoggerEvent import no.nordicsemi.android.gls.main.view.OpenLoggerEvent
import no.nordicsemi.android.gls.main.view.WorkingState import no.nordicsemi.android.gls.main.view.WorkingState
import no.nordicsemi.android.gls.repository.GLSRepository import no.nordicsemi.android.gls.repository.GLSRepository
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.service.ConnectedResult
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
import javax.inject.Inject import javax.inject.Inject
@@ -79,7 +78,7 @@ internal class GLSViewModel @Inject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) { private fun handleResult(result: NavigationResult<ServerDevice>) {
when (result) { when (result) {
is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Cancelled -> navigationManager.navigateUp()
is NavigationResult.Success -> connectDevice(result.value) is NavigationResult.Success -> connectDevice(result.value)
@@ -96,7 +95,7 @@ internal class GLSViewModel @Inject constructor(
} }
} }
private fun connectDevice(device: DiscoveredBluetoothDevice) { private fun connectDevice(device: ServerDevice) {
repository.downloadData(viewModelScope, device).onEach { repository.downloadData(viewModelScope, device).onEach {
_state.value = WorkingState(it) _state.value = WorkingState(it)

View File

@@ -44,10 +44,10 @@ import kotlinx.coroutines.launch
import no.nordicsemi.android.ble.ktx.suspend import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.common.logger.NordicLoggerFactory
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice
import no.nordicsemi.android.gls.data.GLSData import no.nordicsemi.android.gls.data.GLSData
import no.nordicsemi.android.gls.data.GLSManager import no.nordicsemi.android.gls.data.GLSManager
import no.nordicsemi.android.gls.data.WorkingMode import no.nordicsemi.android.gls.data.WorkingMode
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.BleManagerResult
import no.nordicsemi.android.ui.view.StringConst import no.nordicsemi.android.ui.view.StringConst
import javax.inject.Inject import javax.inject.Inject
@@ -63,7 +63,7 @@ internal class GLSRepository @Inject constructor(
private var manager: GLSManager? = null private var manager: GLSManager? = null
private var logger: NordicLogger? = null private var logger: NordicLogger? = null
fun downloadData(scope: CoroutineScope, device: DiscoveredBluetoothDevice): Flow<BleManagerResult<GLSData>> = callbackFlow { fun downloadData(scope: CoroutineScope, device: ServerDevice): Flow<BleManagerResult<GLSData>> = callbackFlow {
val createdLogger = loggerFactory.create(stringConst.APP_NAME, "GLS", device.address).also { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "GLS", device.address).also {
logger = it logger = it
} }
@@ -87,15 +87,15 @@ internal class GLSRepository @Inject constructor(
} }
} }
private suspend fun GLSManager.start(device: DiscoveredBluetoothDevice) { private suspend fun GLSManager.start(device: ServerDevice) {
try { // try {
connect(device.device) // connect(device.device)
.useAutoConnect(false) // .useAutoConnect(false)
.retry(3, 100) // .retry(3, 100)
.suspend() // .suspend()
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} // }
} }
fun openLogger() { fun openLogger() {

View File

@@ -45,6 +45,9 @@ dependencies {
implementation(project(":lib_ui")) implementation(project(":lib_ui"))
implementation(project(":lib_utils")) implementation(project(":lib_utils"))
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1")
implementation(libs.chart) implementation(libs.chart)
implementation(libs.nordic.theme) implementation(libs.nordic.theme)

View File

@@ -40,12 +40,11 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.common.logger.NordicLoggerFactory
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice
import no.nordicsemi.android.hrs.data.HRSData import no.nordicsemi.android.hrs.data.HRSData
import no.nordicsemi.android.hrs.data.HRSManager import no.nordicsemi.android.hrs.data.HRSManager
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.BleManagerResult
import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.IdleResult
import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.service.ServiceManager
@@ -70,11 +69,11 @@ class HRSRepository @Inject constructor(
val isRunning = data.map { it.isRunning() } val isRunning = data.map { it.isRunning() }
val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } val hasBeenDisconnected = data.map { it.hasBeenDisconnected() }
fun launch(device: DiscoveredBluetoothDevice) { fun launch(device: ServerDevice) {
serviceManager.startService(HRSService::class.java, device) serviceManager.startService(HRSService::class.java, device)
} }
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { fun start(device: ServerDevice, scope: CoroutineScope) {
val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HRS", device.address).also { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HRS", device.address).also {
logger = it logger = it
} }
@@ -94,15 +93,15 @@ class HRSRepository @Inject constructor(
NordicLogger.launch(context, logger) NordicLogger.launch(context, logger)
} }
private suspend fun HRSManager.start(device: DiscoveredBluetoothDevice) { private suspend fun HRSManager.start(device: ServerDevice) {
try { // try {
connect(device.device) // connect(device.device)
.useAutoConnect(false) // .useAutoConnect(false)
.retry(3, 100) // .retry(3, 100)
.suspend() // .suspend()
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} // }
} }
fun release() { fun release() {

View File

@@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService import no.nordicsemi.android.service.NotificationService
import javax.inject.Inject import javax.inject.Inject
@@ -50,7 +50,7 @@ internal class HRSService : NotificationService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
val device = intent!!.getParcelableExtra<DiscoveredBluetoothDevice>(DEVICE_DATA)!! val device = intent!!.getParcelableExtra<ServerDevice>(DEVICE_DATA)!!
repository.start(device, lifecycleScope) repository.start(device, lifecycleScope)

View File

@@ -34,7 +34,6 @@ package no.nordicsemi.android.hrs.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton

View File

@@ -46,7 +46,6 @@ import no.nordicsemi.android.analytics.Profile
import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.analytics.ProfileConnectedEvent
import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice
import no.nordicsemi.android.hrs.data.HRS_SERVICE_UUID import no.nordicsemi.android.hrs.data.HRS_SERVICE_UUID
import no.nordicsemi.android.hrs.service.HRSRepository import no.nordicsemi.android.hrs.service.HRSRepository
import no.nordicsemi.android.hrs.view.DisconnectEvent import no.nordicsemi.android.hrs.view.DisconnectEvent
@@ -57,6 +56,7 @@ import no.nordicsemi.android.hrs.view.NoDeviceState
import no.nordicsemi.android.hrs.view.OpenLoggerEvent import no.nordicsemi.android.hrs.view.OpenLoggerEvent
import no.nordicsemi.android.hrs.view.SwitchZoomEvent import no.nordicsemi.android.hrs.view.SwitchZoomEvent
import no.nordicsemi.android.hrs.view.WorkingState import no.nordicsemi.android.hrs.view.WorkingState
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.service.ConnectedResult
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
import javax.inject.Inject import javax.inject.Inject
@@ -96,7 +96,7 @@ internal class HRSViewModel @Inject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) { private fun handleResult(result: NavigationResult<ServerDevice>) {
when (result) { when (result) {
is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Cancelled -> navigationManager.navigateUp()
is NavigationResult.Success -> repository.launch(result.value) is NavigationResult.Success -> repository.launch(result.value)

View File

@@ -45,6 +45,9 @@ dependencies {
implementation(project(":lib_ui")) implementation(project(":lib_ui"))
implementation(project(":lib_utils")) implementation(project(":lib_utils"))
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1")
implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.common)
implementation(libs.nordic.ble.ktx) implementation(libs.nordic.ble.ktx)

View File

@@ -40,12 +40,11 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.common.logger.NordicLoggerFactory
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice
import no.nordicsemi.android.hts.data.HTSData import no.nordicsemi.android.hts.data.HTSData
import no.nordicsemi.android.hts.data.HTSManager import no.nordicsemi.android.hts.data.HTSManager
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.BleManagerResult
import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.IdleResult
import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.service.ServiceManager
@@ -70,11 +69,11 @@ class HTSRepository @Inject constructor(
val isRunning = data.map { it.isRunning() } val isRunning = data.map { it.isRunning() }
val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } val hasBeenDisconnected = data.map { it.hasBeenDisconnected() }
fun launch(device: DiscoveredBluetoothDevice) { fun launch(device: ServerDevice) {
serviceManager.startService(HTSService::class.java, device) serviceManager.startService(HTSService::class.java, device)
} }
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { fun start(device: ServerDevice, scope: CoroutineScope) {
val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HTS", device.address).also { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "HTS", device.address).also {
logger = it logger = it
} }
@@ -94,15 +93,15 @@ class HTSRepository @Inject constructor(
NordicLogger.launch(context, logger) NordicLogger.launch(context, logger)
} }
private suspend fun HTSManager.start(device: DiscoveredBluetoothDevice) { private suspend fun HTSManager.start(device: ServerDevice) {
try { // try {
connect(device.device) // connect(device.device)
.useAutoConnect(false) // .useAutoConnect(false)
.retry(3, 100) // .retry(3, 100)
.suspend() // .suspend()
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} // }
} }
fun release() { fun release() {

View File

@@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService import no.nordicsemi.android.service.NotificationService
import javax.inject.Inject import javax.inject.Inject
@@ -50,7 +50,7 @@ internal class HTSService : NotificationService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
val device = intent!!.getParcelableExtra<DiscoveredBluetoothDevice>(DEVICE_DATA)!! val device = intent!!.getParcelableExtra<ServerDevice>(DEVICE_DATA)!!
repository.start(device, lifecycleScope) repository.start(device, lifecycleScope)

View File

@@ -35,7 +35,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable

View File

@@ -46,7 +46,6 @@ import no.nordicsemi.android.analytics.Profile
import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.analytics.ProfileConnectedEvent
import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice
import no.nordicsemi.android.hts.data.HTS_SERVICE_UUID import no.nordicsemi.android.hts.data.HTS_SERVICE_UUID
import no.nordicsemi.android.hts.repository.HTSRepository import no.nordicsemi.android.hts.repository.HTSRepository
import no.nordicsemi.android.hts.view.DisconnectEvent import no.nordicsemi.android.hts.view.DisconnectEvent
@@ -56,6 +55,7 @@ import no.nordicsemi.android.hts.view.NavigateUp
import no.nordicsemi.android.hts.view.OnTemperatureUnitSelected import no.nordicsemi.android.hts.view.OnTemperatureUnitSelected
import no.nordicsemi.android.hts.view.OpenLoggerEvent import no.nordicsemi.android.hts.view.OpenLoggerEvent
import no.nordicsemi.android.hts.view.WorkingState import no.nordicsemi.android.hts.view.WorkingState
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.service.ConnectedResult
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
import javax.inject.Inject import javax.inject.Inject
@@ -94,7 +94,7 @@ internal class HTSViewModel @Inject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) { private fun handleResult(result: NavigationResult<ServerDevice>) {
when (result) { when (result) {
is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Cancelled -> navigationManager.navigateUp()
is NavigationResult.Success -> repository.launch(result.value) is NavigationResult.Success -> repository.launch(result.value)

View File

@@ -45,6 +45,9 @@ dependencies {
implementation(project(":lib_ui")) implementation(project(":lib_ui"))
implementation(project(":lib_utils")) implementation(project(":lib_utils"))
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1")
implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.common)
implementation(libs.nordic.ble.ktx) implementation(libs.nordic.ble.ktx)

View File

@@ -41,7 +41,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.common.logger.NordicLoggerFactory
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.prx.data.AlarmLevel import no.nordicsemi.android.prx.data.AlarmLevel
import no.nordicsemi.android.prx.data.PRXData import no.nordicsemi.android.prx.data.PRXData
import no.nordicsemi.android.prx.data.PRXManager import no.nordicsemi.android.prx.data.PRXManager
@@ -75,12 +75,12 @@ class PRXRepository @Inject internal constructor(
val isRunning = data.map { it.isRunning() } val isRunning = data.map { it.isRunning() }
val hasBeenDisconnectedWithoutLinkLoss = data.map { it.hasBeenDisconnectedWithoutLinkLoss() } val hasBeenDisconnectedWithoutLinkLoss = data.map { it.hasBeenDisconnectedWithoutLinkLoss() }
fun launch(device: DiscoveredBluetoothDevice) { fun launch(device: ServerDevice) {
serviceManager.startService(PRXService::class.java, device) serviceManager.startService(PRXService::class.java, device)
proximityServerManager.open() proximityServerManager.open()
} }
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { fun start(device: ServerDevice, scope: CoroutineScope) {
val createdLogger = loggerFactory.create(stringConst.APP_NAME, "PRX", device.address).also { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "PRX", device.address).also {
logger = it logger = it
} }
@@ -93,10 +93,10 @@ class PRXRepository @Inject internal constructor(
handleLocalAlarm(it) handleLocalAlarm(it)
}.launchIn(scope) }.launchIn(scope)
manager.connect(device.device) // manager.connect(device.device)
.useAutoConnect(true) // .useAutoConnect(true)
.retry(3, 100) // .retry(3, 100)
.enqueue() // .enqueue()
} }
private fun handleLocalAlarm(result: BleManagerResult<PRXData>) { private fun handleLocalAlarm(result: BleManagerResult<PRXData>) {

View File

@@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService import no.nordicsemi.android.service.NotificationService
import javax.inject.Inject import javax.inject.Inject
@@ -50,7 +50,7 @@ internal class PRXService : NotificationService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
val device = intent!!.getParcelableExtra<DiscoveredBluetoothDevice>(DEVICE_DATA)!! val device = intent!!.getParcelableExtra<ServerDevice>(DEVICE_DATA)!!
repository.start(device, lifecycleScope) repository.start(device, lifecycleScope)

View File

@@ -34,7 +34,6 @@ package no.nordicsemi.android.prx.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Button import androidx.compose.material3.Button

View File

@@ -39,7 +39,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp

View File

@@ -46,7 +46,7 @@ import no.nordicsemi.android.analytics.Profile
import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.analytics.ProfileConnectedEvent
import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.prx.data.PRX_SERVICE_UUID import no.nordicsemi.android.prx.data.PRX_SERVICE_UUID
import no.nordicsemi.android.prx.repository.PRXRepository import no.nordicsemi.android.prx.repository.PRXRepository
import no.nordicsemi.android.prx.view.DisconnectEvent import no.nordicsemi.android.prx.view.DisconnectEvent
@@ -96,7 +96,7 @@ internal class PRXViewModel @Inject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) { private fun handleResult(result: NavigationResult<ServerDevice>) {
when (result) { when (result) {
is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Cancelled -> navigationManager.navigateUp()
is NavigationResult.Success -> repository.launch(result.value) is NavigationResult.Success -> repository.launch(result.value)

View File

@@ -45,6 +45,9 @@ dependencies {
implementation(project(":lib_ui")) implementation(project(":lib_ui"))
implementation(project(":lib_utils")) implementation(project(":lib_utils"))
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1")
implementation(libs.nordic.ble.common) implementation(libs.nordic.ble.common)
implementation(libs.nordic.ble.ktx) implementation(libs.nordic.ble.ktx)

View File

@@ -40,10 +40,9 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.common.logger.NordicLoggerFactory
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.rscs.data.RSCSData import no.nordicsemi.android.rscs.data.RSCSData
import no.nordicsemi.android.rscs.data.RSCSManager import no.nordicsemi.android.rscs.data.RSCSManager
import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.BleManagerResult
@@ -70,11 +69,11 @@ class RSCSRepository @Inject constructor(
val isRunning = data.map { it.isRunning() } val isRunning = data.map { it.isRunning() }
val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } val hasBeenDisconnected = data.map { it.hasBeenDisconnected() }
fun launch(device: DiscoveredBluetoothDevice) { fun launch(device: ServerDevice) {
serviceManager.startService(RSCSService::class.java, device) serviceManager.startService(RSCSService::class.java, device)
} }
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { fun start(device: ServerDevice, scope: CoroutineScope) {
val createdLogger = loggerFactory.create(stringConst.APP_NAME, "RSCS", device.address).also { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "RSCS", device.address).also {
logger = it logger = it
} }
@@ -94,15 +93,15 @@ class RSCSRepository @Inject constructor(
NordicLogger.launch(context, logger) NordicLogger.launch(context, logger)
} }
private suspend fun RSCSManager.start(device: DiscoveredBluetoothDevice) { private suspend fun RSCSManager.start(device: ServerDevice) {
try { // try {
connect(device.device) // connect(device.device)
.useAutoConnect(false) // .useAutoConnect(false)
.retry(3, 100) // .retry(3, 100)
.suspend() // .suspend()
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} // }
} }
fun release() { fun release() {

View File

@@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService import no.nordicsemi.android.service.NotificationService
import javax.inject.Inject import javax.inject.Inject
@@ -50,7 +50,7 @@ internal class RSCSService : NotificationService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
val device = intent!!.getParcelableExtra<DiscoveredBluetoothDevice>(DEVICE_DATA)!! val device = intent!!.getParcelableExtra<ServerDevice>(DEVICE_DATA)!!
repository.start(device, lifecycleScope) repository.start(device, lifecycleScope)

View File

@@ -33,7 +33,6 @@ package no.nordicsemi.android.rscs.view
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.material3.OutlinedCard
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource

View File

@@ -46,7 +46,7 @@ import no.nordicsemi.android.analytics.Profile
import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.analytics.ProfileConnectedEvent
import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.rscs.data.RSCS_SERVICE_UUID import no.nordicsemi.android.rscs.data.RSCS_SERVICE_UUID
import no.nordicsemi.android.rscs.repository.RSCSRepository import no.nordicsemi.android.rscs.repository.RSCSRepository
import no.nordicsemi.android.rscs.view.DisconnectEvent import no.nordicsemi.android.rscs.view.DisconnectEvent
@@ -94,7 +94,7 @@ internal class RSCSViewModel @Inject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) { private fun handleResult(result: NavigationResult<ServerDevice>) {
when (result) { when (result) {
is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Cancelled -> navigationManager.navigateUp()
is NavigationResult.Success -> repository.launch(result.value) is NavigationResult.Success -> repository.launch(result.value)

View File

@@ -51,6 +51,9 @@ dependencies {
implementation(project(":lib_ui")) implementation(project(":lib_ui"))
implementation(project(":lib_utils")) implementation(project(":lib_utils"))
implementation("no.nordicsemi.android.kotlin.ble:core:0.0.1")
implementation("no.nordicsemi.android.kotlin.ble:profile:0.0.1")
implementation(libs.room.runtime) implementation(libs.room.runtime)
implementation(libs.room.ktx) implementation(libs.room.ktx)
kapt(libs.room.compiler) kapt(libs.room.compiler)

View File

@@ -35,9 +35,8 @@ import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Root; import org.simpleframework.xml.Root;
import org.simpleframework.xml.Text; import org.simpleframework.xml.Text;
//import no.nordicsemi.android.uart.data.MacroEol; import no.nordicsemi.android.uart.data.MacroEol;
//import no.nordicsemi.android.uart.data.MacroIcon; import no.nordicsemi.android.uart.data.MacroIcon;
import no.nordicsemi.android.uart.data.*;
@Root @Root
public class XmlMacro { public class XmlMacro {

View File

@@ -40,10 +40,9 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import no.nordicsemi.android.ble.ktx.suspend
import no.nordicsemi.android.common.logger.NordicLogger import no.nordicsemi.android.common.logger.NordicLogger
import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.common.logger.NordicLoggerFactory
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.BleManagerResult import no.nordicsemi.android.service.BleManagerResult
import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.IdleResult
import no.nordicsemi.android.service.ServiceManager import no.nordicsemi.android.service.ServiceManager
@@ -78,11 +77,11 @@ class UARTRepository @Inject internal constructor(
val lastConfigurationName = configurationDataSource.lastConfigurationName val lastConfigurationName = configurationDataSource.lastConfigurationName
fun launch(device: DiscoveredBluetoothDevice) { fun launch(device: ServerDevice) {
serviceManager.startService(UARTService::class.java, device) serviceManager.startService(UARTService::class.java, device)
} }
fun start(device: DiscoveredBluetoothDevice, scope: CoroutineScope) { fun start(device: ServerDevice, scope: CoroutineScope) {
val createdLogger = loggerFactory.create(stringConst.APP_NAME, "UART", device.address).also { val createdLogger = loggerFactory.create(stringConst.APP_NAME, "UART", device.address).also {
logger = it logger = it
} }
@@ -119,15 +118,15 @@ class UARTRepository @Inject internal constructor(
configurationDataSource.saveConfigurationName(name) configurationDataSource.saveConfigurationName(name)
} }
private suspend fun UARTManager.start(device: DiscoveredBluetoothDevice) { private suspend fun UARTManager.start(device: ServerDevice) {
try { // try {
connect(device.device) // connect(device.device)
.useAutoConnect(false) // .useAutoConnect(false)
.retry(3, 100) // .retry(3, 100)
.suspend() // .suspend()
} catch (e: Exception) { // } catch (e: Exception) {
e.printStackTrace() // e.printStackTrace()
} // }
} }
fun release() { fun release() {

View File

@@ -36,7 +36,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.DEVICE_DATA
import no.nordicsemi.android.service.NotificationService import no.nordicsemi.android.service.NotificationService
import javax.inject.Inject import javax.inject.Inject
@@ -50,7 +50,7 @@ internal class UARTService : NotificationService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
val device = intent!!.getParcelableExtra<DiscoveredBluetoothDevice>(DEVICE_DATA)!! val device = intent!!.getParcelableExtra<ServerDevice>(DEVICE_DATA)!!
repository.start(device, lifecycleScope) repository.start(device, lifecycleScope)

View File

@@ -51,7 +51,7 @@ import no.nordicsemi.android.analytics.UARTMode
import no.nordicsemi.android.analytics.UARTSendAnalyticsEvent import no.nordicsemi.android.analytics.UARTSendAnalyticsEvent
import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.NavigationResult
import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.common.navigation.Navigator
import no.nordicsemi.android.common.ui.scanner.model.DiscoveredBluetoothDevice import no.nordicsemi.android.kotlin.ble.core.ServerDevice
import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.service.ConnectedResult
import no.nordicsemi.android.service.IdleResult import no.nordicsemi.android.service.IdleResult
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
@@ -129,7 +129,7 @@ internal class UARTViewModel @Inject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun handleResult(result: NavigationResult<DiscoveredBluetoothDevice>) { private fun handleResult(result: NavigationResult<ServerDevice>) {
when (result) { when (result) {
is NavigationResult.Cancelled -> navigationManager.navigateUp() is NavigationResult.Cancelled -> navigationManager.navigateUp()
is NavigationResult.Success -> repository.launch(result.value) is NavigationResult.Success -> repository.launch(result.value)