diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt index 40f9bdb1..81a23ac0 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/data/HRSServiceData.kt @@ -38,7 +38,9 @@ internal data class HRSServiceData( val data: List = emptyList(), val bodySensorLocation: Int? = null, val batteryLevel: Int? = null, - val connectionState: GattConnectionState? = null + val connectionState: GattConnectionState? = null, + val zoomIn: Boolean = false, + val deviceName: String? = null ) { val heartRates = data.map { it.heartRate } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt index 9f3e8ff5..829e3c9f 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSRepository.kt @@ -68,6 +68,14 @@ class HRSRepository @Inject constructor( serviceManager.startService(HRSService::class.java, device) } + fun onInitComplete(device: ServerDevice) { + _data.value = _data.value.copy(deviceName = device.name) + } + + fun switchZoomIn() { + _data.value = _data.value.copy(zoomIn = !_data.value.zoomIn) + } + fun onConnectionStateChanged(connectionState: GattConnectionState?) { _data.value = _data.value.copy(connectionState = connectionState) } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt index 137cfc4e..4240f176 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/service/HRSService.kt @@ -93,11 +93,11 @@ internal class HRSService : NotificationService() { client.services .filterNotNull() - .onEach { configureGatt(it) } + .onEach { configureGatt(it, device) } .launchIn(lifecycleScope) } - private suspend fun configureGatt(services: BleGattServices) { + private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) { val htsService = services.findService(HRS_SERVICE_UUID)!! val htsMeasurementCharacteristic = htsService.findCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID)!! val bodySensorLocationCharacteristic = htsService.findCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID)!! @@ -116,6 +116,8 @@ internal class HRSService : NotificationService() { .mapNotNull { HRSDataParser.parse(it) } .onEach { repository.onHRSDataChanged(it) } .launchIn(lifecycleScope) + + repository.onInitComplete(device) } private fun stopIfDisconnected(connectionState: GattConnectionState) { diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt index 3b653850..52dbc95c 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSContentView.kt @@ -52,7 +52,7 @@ import no.nordicsemi.android.ui.view.ScreenSection import no.nordicsemi.android.ui.view.SectionTitle @Composable -internal fun HRSContentView(state: HRSServiceData, zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) { +internal fun HRSContentView(state: HRSServiceData, onEvent: (HRSScreenViewEvent) -> Unit) { Column( horizontalAlignment = Alignment.CenterHorizontally ) { @@ -61,12 +61,12 @@ internal fun HRSContentView(state: HRSServiceData, zoomIn: Boolean, onEvent: (HR SectionTitle( resId = R.drawable.ic_chart_line, title = stringResource(id = R.string.hrs_section_data), - menu = { Menu(zoomIn, onEvent) } + menu = { Menu(state.zoomIn, onEvent) } ) Spacer(modifier = Modifier.height(16.dp)) - LineChartView(state, zoomIn) + LineChartView(state, state.zoomIn) } Spacer(modifier = Modifier.height(16.dp)) @@ -102,5 +102,5 @@ private fun Menu(zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) { @Preview @Composable private fun Preview() { - HRSContentView(state = HRSServiceData(), zoomIn = false) { } + HRSContentView(state = HRSServiceData()) { } } diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt index c37c2882..3a6b508e 100644 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt +++ b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSScreen.kt @@ -47,6 +47,7 @@ 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.hrs.R +import no.nordicsemi.android.hrs.data.HRSServiceData import no.nordicsemi.android.hrs.viewmodel.HRSViewModel import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.ui.view.BackIconAppBar @@ -70,14 +71,15 @@ fun HRSScreen() { .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - when (state.hrsManagerState) { - NoDeviceState -> DeviceConnectingView() - is WorkingState -> when (state.hrsManagerState.result.connectionState) { + if (state.deviceName == null) { + DeviceConnectingView() + } else { + when (state.connectionState) { null, GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) } GattConnectionState.STATE_DISCONNECTED, GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) } - GattConnectionState.STATE_CONNECTED -> HRSContentView(state.hrsManagerState.result, state.zoomIn) { viewModel.onEvent(it) } + GattConnectionState.STATE_CONNECTED -> HRSContentView(state) { viewModel.onEvent(it) } } } } @@ -85,7 +87,7 @@ fun HRSScreen() { } @Composable -private fun AppBar(state: HRSViewState, navigateUp: () -> Unit, viewModel: HRSViewModel) { +private fun AppBar(state: HRSServiceData, navigateUp: () -> Unit, viewModel: HRSViewModel) { if (state.deviceName?.isNotBlank() == true) { LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) { viewModel.onEvent(OpenLoggerEvent) diff --git a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt b/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt deleted file mode 100644 index d0d3e597..00000000 --- a/profile_hrs/src/main/java/no/nordicsemi/android/hrs/view/HRSState.kt +++ /dev/null @@ -1,46 +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.hrs.view - -import no.nordicsemi.android.hrs.data.HRSServiceData - -internal data class HRSViewState( - val zoomIn: Boolean = false, - val hrsManagerState: HRSManagerState = NoDeviceState, - val deviceName: String? = null -) - -internal sealed class HRSManagerState - -internal data class WorkingState(val result: HRSServiceData) : HRSManagerState() - -internal object NoDeviceState : HRSManagerState() 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 d2237b36..5de6a338 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 @@ -35,8 +35,6 @@ import android.os.ParcelUuid import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -50,11 +48,9 @@ import no.nordicsemi.android.hrs.service.HRSRepository import no.nordicsemi.android.hrs.service.HRS_SERVICE_UUID import no.nordicsemi.android.hrs.view.DisconnectEvent import no.nordicsemi.android.hrs.view.HRSScreenViewEvent -import no.nordicsemi.android.hrs.view.HRSViewState import no.nordicsemi.android.hrs.view.NavigateUpEvent import no.nordicsemi.android.hrs.view.OpenLoggerEvent import no.nordicsemi.android.hrs.view.SwitchZoomEvent -import no.nordicsemi.android.hrs.view.WorkingState import no.nordicsemi.android.kotlin.ble.core.ServerDevice import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId @@ -67,8 +63,7 @@ internal class HRSViewModel @Inject constructor( private val analytics: AppAnalytics ) : ViewModel() { - private val _state = MutableStateFlow(HRSViewState()) - val state = _state.asStateFlow() + val state = repository.data init { viewModelScope.launch { @@ -78,8 +73,6 @@ internal class HRSViewModel @Inject constructor( } repository.data.onEach { - _state.value = _state.value.copy(hrsManagerState = WorkingState(it)) - if (it.connectionState == GattConnectionState.STATE_CONNECTED) { analytics.logEvent(ProfileConnectedEvent(Profile.HRS)) } @@ -102,7 +95,6 @@ internal class HRSViewModel @Inject constructor( } private fun onDeviceSelected(device: ServerDevice) { - _state.value = _state.value.copy(deviceName = device.name) repository.launch(device) } @@ -116,7 +108,7 @@ internal class HRSViewModel @Inject constructor( } private fun onZoomButtonClicked() { - _state.value = _state.value.copy(zoomIn = !_state.value.zoomIn) + repository.switchZoomIn() } private fun disconnect() {