diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt index 6e14805b..d2237b36 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/viewmodel/HRSViewModel.kt @@ -97,10 +97,15 @@ internal class HRSViewModel @Inject constructor( private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() - is NavigationResult.Success -> repository.launch(result.value) + is NavigationResult.Success -> onDeviceSelected(result.value) } } + private fun onDeviceSelected(device: ServerDevice) { + _state.value = _state.value.copy(deviceName = device.name) + repository.launch(device) + } + fun onEvent(event: HRSScreenViewEvent) { when (event) { DisconnectEvent -> disconnect() diff --git a/profile_rscs/build.gradle.kts b/profile_rscs/build.gradle.kts index 360d8826..513505bc 100644 --- a/profile_rscs/build.gradle.kts +++ b/profile_rscs/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { implementation(libs.nordic.uiscanner) implementation(libs.nordic.navigation) implementation(libs.nordic.uilogger) + implementation(libs.nordic.core) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.material.iconsExtended) diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSManager.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSManager.kt deleted file mode 100644 index 30668a64..00000000 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSManager.kt +++ /dev/null @@ -1,126 +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.rscs.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.rsc.RunningSpeedAndCadenceMeasurementResponse -import no.nordicsemi.android.ble.ktx.asValidResponseFlow -import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.service.ConnectionObserverAdapter -import java.util.* - -val RSCS_SERVICE_UUID: UUID = UUID.fromString("00001814-0000-1000-8000-00805F9B34FB") -private val RSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A53-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 RSCSManager internal constructor( - context: Context, - private val scope: CoroutineScope, - private val logger: NordicLogger -) : BleManager(context) { - - private var batteryLevelCharacteristic: BluetoothGattCharacteristic? = null - private var rscMeasurementCharacteristic: BluetoothGattCharacteristic? = null - - private val data = MutableStateFlow(RSCSData()) - val dataHolder = ConnectionObserverAdapter() - - 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 - } - - private inner class RSCManagerGattCallback : BleManagerGattCallback() { - - override fun initialize() { - super.initialize() - setNotificationCallback(rscMeasurementCharacteristic).asValidResponseFlow() - .onEach { - data.tryEmit(data.value.copy( - running = it.isRunning, - instantaneousCadence = it.instantaneousCadence, - instantaneousSpeed = it.instantaneousSpeed, - strideLength = it.strideLength, - totalDistance = it.totalDistance - )) - }.launchIn(scope) - enableNotifications(rscMeasurementCharacteristic).enqueue() - - setNotificationCallback(batteryLevelCharacteristic) - .asValidResponseFlow() - .onEach { - data.value = data.value.copy(batteryLevel = it.batteryLevel) - }.launchIn(scope) - enableNotifications(batteryLevelCharacteristic).enqueue() - } - - public override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean { - gatt.getService(RSCS_SERVICE_UUID)?.run { - rscMeasurementCharacteristic = getCharacteristic(RSC_MEASUREMENT_CHARACTERISTIC_UUID) - } - gatt.getService(BATTERY_SERVICE_UUID)?.run { - batteryLevelCharacteristic = getCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID) - } - return rscMeasurementCharacteristic != null - } - - override fun onServicesInvalidated() { - rscMeasurementCharacteristic = null - batteryLevelCharacteristic = null - } - } - - override fun getGattCallback(): BleManagerGattCallback { - return RSCManagerGattCallback() - } -} diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSData.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt similarity index 65% rename from profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSData.kt rename to profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt index 75f303ea..8c2578a0 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSData.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/data/RSCSServiceData.kt @@ -31,37 +31,42 @@ package no.nordicsemi.android.rscs.data -internal data class RSCSData( - val batteryLevel: Int? = null, - val running: Boolean = false, - val instantaneousSpeed: Float = 1.0f, - val instantaneousCadence: Int = 0, - val strideLength: Int? = null, - val totalDistance: Long? = null -) { +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSData +import no.nordicsemi.android.rscs.R +internal data class RSCSServiceData( + val data: RSCSData = RSCSData(), + val batteryLevel: Int? = null, + val connectionState: GattConnectionState? = null +) { + @Composable fun displayActivity(): String { - return if (running) { - "Running" + return if (data.running) { + stringResource(id = R.string.rscs_running) } else { - "Walking" + stringResource(id = R.string.rscs_walking) } } + @Composable fun displayPace(): String { - return "$instantaneousCadence min/km" + return stringResource(id = R.string.rscs_speed, data.instantaneousSpeed) } - + @Composable fun displayCadence(): String { - return "$instantaneousCadence RPM" + return stringResource(id = R.string.rscs_rpm, data.instantaneousCadence) } + @Composable fun displayNumberOfSteps(): String? { - if (totalDistance == null || strideLength == null) { + if (data.totalDistance == null || data.strideLength == null) { return null } - val numberOfSteps = totalDistance/strideLength - return "Number of Steps $numberOfSteps" + val numberOfSteps = data.totalDistance!! / data.strideLength!!.toLong() + return stringResource(id = R.string.rscs_steps, numberOfSteps) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt index 1e270afb..5490d379 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSRepository.kt @@ -33,22 +33,18 @@ package no.nordicsemi.android.rscs.repository import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import no.nordicsemi.android.common.core.simpleSharedFlow import no.nordicsemi.android.common.logger.NordicLogger -import no.nordicsemi.android.common.logger.NordicLoggerFactory import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.rscs.data.RSCSData -import no.nordicsemi.android.rscs.data.RSCSManager -import no.nordicsemi.android.service.BleManagerResult -import no.nordicsemi.android.service.IdleResult +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSData +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 javax.inject.Inject import javax.inject.Singleton @@ -56,57 +52,40 @@ import javax.inject.Singleton class RSCSRepository @Inject constructor( @ApplicationContext private val context: Context, - private val serviceManager: ServiceManager, - private val loggerFactory: NordicLoggerFactory, - private val stringConst: StringConst + private val serviceManager: ServiceManager ) { - private var manager: RSCSManager? = null private var logger: NordicLogger? = null - private val _data = MutableStateFlow>(IdleResult()) + private val _data = MutableStateFlow(RSCSServiceData()) internal val data = _data.asStateFlow() - val isRunning = data.map { it.isRunning() } - val hasBeenDisconnected = data.map { it.hasBeenDisconnected() } + private val _stopEvent = simpleSharedFlow() + internal val stopEvent = _stopEvent.asSharedFlow() + + val isRunning = data.map { it.connectionState == GattConnectionState.STATE_CONNECTED } fun launch(device: ServerDevice) { serviceManager.startService(RSCSService::class.java, device) } - fun start(device: ServerDevice, scope: CoroutineScope) { - val createdLogger = loggerFactory.create(stringConst.APP_NAME, "RSCS", device.address).also { - logger = it - } - val manager = RSCSManager(context, scope, createdLogger) - this.manager = manager + fun onConnectionStateChanged(connectionState: GattConnectionState?) { + _data.value = _data.value.copy(connectionState = connectionState) + } - manager.dataHolder.status.onEach { - _data.value = it - }.launchIn(scope) + fun onRSCSDataChanged(data: RSCSData) { + _data.value = _data.value.copy(data = data) + } - scope.launch { - manager.start(device) - } + fun onBatteryLevelChanged(batteryLevel: Int) { + _data.value = _data.value.copy(batteryLevel = batteryLevel) } fun openLogger() { NordicLogger.launch(context, logger) } - private suspend fun RSCSManager.start(device: ServerDevice) { -// try { -// connect(device.device) -// .useAutoConnect(false) -// .retry(3, 100) -// .suspend() -// } catch (e: Exception) { -// e.printStackTrace() -// } - } - fun release() { - manager?.disconnect()?.enqueue() - manager = null logger = null + _stopEvent.tryEmit(DisconnectAndStopEvent()) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt index acdb6120..f1236945 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/repository/RSCSService.kt @@ -31,33 +31,94 @@ package no.nordicsemi.android.rscs.repository +import android.annotation.SuppressLint import android.content.Intent import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import no.nordicsemi.android.kotlin.ble.core.ServerDevice +import no.nordicsemi.android.kotlin.ble.core.client.callback.BleGattClient +import no.nordicsemi.android.kotlin.ble.core.client.service.BleGattServices +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState +import no.nordicsemi.android.kotlin.ble.profile.battery.BatteryLevelParser +import no.nordicsemi.android.kotlin.ble.profile.rscs.RSCSDataParser import no.nordicsemi.android.service.DEVICE_DATA import no.nordicsemi.android.service.NotificationService +import java.util.* import javax.inject.Inject +val RSCS_SERVICE_UUID: UUID = UUID.fromString("00001814-0000-1000-8000-00805F9B34FB") +private val RSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A53-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 internal class RSCSService : NotificationService() { @Inject lateinit var repository: RSCSRepository + private lateinit var client: BleGattClient + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) val device = intent!!.getParcelableExtra(DEVICE_DATA)!! - repository.start(device, lifecycleScope) + startGattClient(device) - repository.hasBeenDisconnected.onEach { - if (it) stopSelf() - }.launchIn(lifecycleScope) + repository.stopEvent + .onEach { disconnect() } + .launchIn(lifecycleScope) return START_REDELIVER_INTENT } + + private fun startGattClient(blinkyDevice: ServerDevice) = lifecycleScope.launch { + client = blinkyDevice.connect(this@RSCSService) + + client.connectionState + .onEach { repository.onConnectionStateChanged(it) } + .filterNotNull() + .onEach { stopIfDisconnected(it) } + .launchIn(lifecycleScope) + + client.services + .filterNotNull() + .onEach { configureGatt(it) } + .launchIn(lifecycleScope) + } + + private suspend fun configureGatt(services: BleGattServices) { + val rscsService = services.findService(RSCS_SERVICE_UUID)!! + val rscsMeasurementCharacteristic = rscsService.findCharacteristic(RSC_MEASUREMENT_CHARACTERISTIC_UUID)!! + val batteryService = services.findService(BATTERY_SERVICE_UUID)!! + val batteryLevelCharacteristic = batteryService.findCharacteristic(BATTERY_LEVEL_CHARACTERISTIC_UUID)!! + + batteryLevelCharacteristic.getNotifications() + .mapNotNull { BatteryLevelParser.parse(it) } + .onEach { repository.onBatteryLevelChanged(it) } + .launchIn(lifecycleScope) + + rscsMeasurementCharacteristic.getNotifications() + .mapNotNull { RSCSDataParser.parse(it) } + .onEach { repository.onRSCSDataChanged(it) } + .launchIn(lifecycleScope) + } + + private fun stopIfDisconnected(connectionState: GattConnectionState) { + if (connectionState == GattConnectionState.STATE_DISCONNECTED) { + stopSelf() + } + } + + private fun disconnect() { + client.disconnect() + } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSContentView.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSContentView.kt index 785429c7..f423ebaa 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSContentView.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSContentView.kt @@ -43,11 +43,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.rscs.R -import no.nordicsemi.android.rscs.data.RSCSData +import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.ui.view.BatteryLevelView @Composable -internal fun RSCSContentView(state: RSCSData, onEvent: (RSCScreenViewEvent) -> Unit) { +internal fun RSCSContentView(state: RSCSServiceData, onEvent: (RSCScreenViewEvent) -> Unit) { Column( horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -74,5 +74,5 @@ internal fun RSCSContentView(state: RSCSData, onEvent: (RSCScreenViewEvent) -> U @Preview @Composable private fun RSCSContentViewPreview() { - RSCSContentView(RSCSData()) { } + RSCSContentView(RSCSServiceData()) { } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt index 9825bf92..21beeced 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSScreen.kt @@ -46,17 +46,9 @@ import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.common.ui.scanner.view.DeviceConnectingView import no.nordicsemi.android.common.ui.scanner.view.DeviceDisconnectedView import no.nordicsemi.android.common.ui.scanner.view.Reason +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.rscs.R import no.nordicsemi.android.rscs.viewmodel.RSCSViewModel -import no.nordicsemi.android.service.ConnectedResult -import no.nordicsemi.android.service.ConnectingResult -import no.nordicsemi.android.service.DeviceHolder -import no.nordicsemi.android.service.DisconnectedResult -import no.nordicsemi.android.service.IdleResult -import no.nordicsemi.android.service.LinkLossResult -import no.nordicsemi.android.service.MissingServiceResult -import no.nordicsemi.android.service.SuccessResult -import no.nordicsemi.android.service.UnknownErrorResult import no.nordicsemi.android.ui.view.BackIconAppBar import no.nordicsemi.android.ui.view.LoggerIconAppBar import no.nordicsemi.android.ui.view.NavigateUpButton @@ -78,17 +70,14 @@ fun RSCSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state) { + when (state.rscsManagerState) { NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.result) { - is IdleResult, - is ConnectingResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is ConnectedResult -> DeviceConnectingView { NavigateUpButton(navigateUp) } - is DisconnectedResult -> DeviceDisconnectedView(Reason.USER) { NavigateUpButton(navigateUp) } - 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 -> RSCSContentView(state.result.data) { viewModel.onEvent(it) } + is WorkingState -> when (state.rscsManagerState.result.connectionState) { + null, + GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_DISCONNECTED, + GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } + GattConnectionState.STATE_CONNECTED -> RSCSContentView(state.rscsManagerState.result) { viewModel.onEvent(it) } } } } @@ -97,15 +86,11 @@ fun RSCSScreen() { @Composable private fun AppBar(state: RSCSViewState, navigateUp: () -> Unit, viewModel: RSCSViewModel) { - val toolbarName = (state as? WorkingState)?.let { - (it.result as? DeviceHolder)?.deviceName() - } - - if (toolbarName == null) { - BackIconAppBar(stringResource(id = R.string.rscs_title), navigateUp) - } else { - LoggerIconAppBar(toolbarName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { + if (state.deviceName?.isNotBlank() == true) { + LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) } + } else { + BackIconAppBar(stringResource(id = R.string.rscs_title), navigateUp) } } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt index 23084a89..56710f1f 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/RSCSState.kt @@ -31,11 +31,15 @@ package no.nordicsemi.android.rscs.view -import no.nordicsemi.android.rscs.data.RSCSData -import no.nordicsemi.android.service.BleManagerResult +import no.nordicsemi.android.rscs.data.RSCSServiceData -internal sealed class RSCSViewState +internal data class RSCSViewState( + val rscsManagerState: RSCSManagerState = NoDeviceState, + val deviceName: String? = null +) -internal data class WorkingState(val result: BleManagerResult) : RSCSViewState() +internal sealed class RSCSManagerState -internal object NoDeviceState : RSCSViewState() +internal data class WorkingState(val result: RSCSServiceData) : RSCSManagerState() + +internal object NoDeviceState : RSCSManagerState() diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt index 241c1c1e..54360961 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/view/SensorsReadingView.kt @@ -39,13 +39,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import no.nordicsemi.android.rscs.R -import no.nordicsemi.android.rscs.data.RSCSData +import no.nordicsemi.android.rscs.data.RSCSServiceData import no.nordicsemi.android.ui.view.KeyValueField import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun SensorsReadingView(state: RSCSData) { +internal fun SensorsReadingView(state: RSCSServiceData) { ScreenSection { SectionTitle(resId = R.drawable.ic_records, title = "Records") @@ -66,5 +66,5 @@ internal fun SensorsReadingView(state: RSCSData) { @Preview @Composable private fun Preview() { - SensorsReadingView(RSCSData()) + SensorsReadingView(RSCSServiceData()) } diff --git a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt index 78360059..3ac505e8 100644 --- a/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt +++ b/profile_rscs/src/main/java/no/nordicsemi/android/rscs/viewmodel/RSCSViewModel.kt @@ -47,16 +47,15 @@ import no.nordicsemi.android.analytics.ProfileConnectedEvent import no.nordicsemi.android.common.navigation.NavigationResult import no.nordicsemi.android.common.navigation.Navigator import no.nordicsemi.android.kotlin.ble.core.ServerDevice -import no.nordicsemi.android.rscs.data.RSCS_SERVICE_UUID +import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.rscs.repository.RSCSRepository +import no.nordicsemi.android.rscs.repository.RSCS_SERVICE_UUID import no.nordicsemi.android.rscs.view.DisconnectEvent import no.nordicsemi.android.rscs.view.NavigateUpEvent -import no.nordicsemi.android.rscs.view.NoDeviceState import no.nordicsemi.android.rscs.view.OpenLoggerEvent import no.nordicsemi.android.rscs.view.RSCSViewState import no.nordicsemi.android.rscs.view.RSCScreenViewEvent import no.nordicsemi.android.rscs.view.WorkingState -import no.nordicsemi.android.service.ConnectedResult import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId import javax.inject.Inject @@ -67,7 +66,7 @@ internal class RSCSViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(NoDeviceState) + private val _state = MutableStateFlow(RSCSViewState()) val state = _state.asStateFlow() init { @@ -78,9 +77,9 @@ internal class RSCSViewModel @Inject constructor( } repository.data.onEach { - _state.value = WorkingState(it) + _state.value = _state.value.copy(rscsManagerState = WorkingState(it)) - (it as? ConnectedResult)?.let { + if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.RSCS)) } }.launchIn(viewModelScope) @@ -97,10 +96,15 @@ internal class RSCSViewModel @Inject constructor( private fun handleResult(result: NavigationResult) { when (result) { is NavigationResult.Cancelled -> navigationManager.navigateUp() - is NavigationResult.Success -> repository.launch(result.value) + is NavigationResult.Success -> onDeviceSelected(result.value) } } + private fun onDeviceSelected(device: ServerDevice) { + _state.value = _state.value.copy(deviceName = device.name) + repository.launch(device) + } + fun onEvent(event: RSCScreenViewEvent) { when (event) { DisconnectEvent -> disconnect() diff --git a/profile_rscs/src/main/res/values/strings.xml b/profile_rscs/src/main/res/values/strings.xml index 35cf1e57..eb061fc4 100644 --- a/profile_rscs/src/main/res/values/strings.xml +++ b/profile_rscs/src/main/res/values/strings.xml @@ -37,4 +37,10 @@ Pace Cadence Number of steps + + Walking + Running + %.1f min/km + %d RPM + Number of Steps %d