mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-22 08:54:21 +01:00
Fix HRS profile
This commit is contained in:
@@ -38,7 +38,9 @@ internal data class HRSServiceData(
|
|||||||
val data: List<HRSData> = emptyList(),
|
val data: List<HRSData> = emptyList(),
|
||||||
val bodySensorLocation: Int? = null,
|
val bodySensorLocation: Int? = null,
|
||||||
val batteryLevel: 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 }
|
val heartRates = data.map { it.heartRate }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ class HRSRepository @Inject constructor(
|
|||||||
serviceManager.startService(HRSService::class.java, device)
|
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?) {
|
fun onConnectionStateChanged(connectionState: GattConnectionState?) {
|
||||||
_data.value = _data.value.copy(connectionState = connectionState)
|
_data.value = _data.value.copy(connectionState = connectionState)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,11 +93,11 @@ internal class HRSService : NotificationService() {
|
|||||||
|
|
||||||
client.services
|
client.services
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.onEach { configureGatt(it) }
|
.onEach { configureGatt(it, device) }
|
||||||
.launchIn(lifecycleScope)
|
.launchIn(lifecycleScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun configureGatt(services: BleGattServices) {
|
private suspend fun configureGatt(services: BleGattServices, device: ServerDevice) {
|
||||||
val htsService = services.findService(HRS_SERVICE_UUID)!!
|
val htsService = services.findService(HRS_SERVICE_UUID)!!
|
||||||
val htsMeasurementCharacteristic = htsService.findCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID)!!
|
val htsMeasurementCharacteristic = htsService.findCharacteristic(HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID)!!
|
||||||
val bodySensorLocationCharacteristic = htsService.findCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID)!!
|
val bodySensorLocationCharacteristic = htsService.findCharacteristic(BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID)!!
|
||||||
@@ -116,6 +116,8 @@ internal class HRSService : NotificationService() {
|
|||||||
.mapNotNull { HRSDataParser.parse(it) }
|
.mapNotNull { HRSDataParser.parse(it) }
|
||||||
.onEach { repository.onHRSDataChanged(it) }
|
.onEach { repository.onHRSDataChanged(it) }
|
||||||
.launchIn(lifecycleScope)
|
.launchIn(lifecycleScope)
|
||||||
|
|
||||||
|
repository.onInitComplete(device)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopIfDisconnected(connectionState: GattConnectionState) {
|
private fun stopIfDisconnected(connectionState: GattConnectionState) {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ 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 HRSContentView(state: HRSServiceData, zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) {
|
internal fun HRSContentView(state: HRSServiceData, onEvent: (HRSScreenViewEvent) -> Unit) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
@@ -61,12 +61,12 @@ internal fun HRSContentView(state: HRSServiceData, zoomIn: Boolean, onEvent: (HR
|
|||||||
SectionTitle(
|
SectionTitle(
|
||||||
resId = R.drawable.ic_chart_line,
|
resId = R.drawable.ic_chart_line,
|
||||||
title = stringResource(id = R.string.hrs_section_data),
|
title = stringResource(id = R.string.hrs_section_data),
|
||||||
menu = { Menu(zoomIn, onEvent) }
|
menu = { Menu(state.zoomIn, onEvent) }
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
LineChartView(state, zoomIn)
|
LineChartView(state, state.zoomIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
@@ -102,5 +102,5 @@ private fun Menu(zoomIn: Boolean, onEvent: (HRSScreenViewEvent) -> Unit) {
|
|||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun Preview() {
|
private fun Preview() {
|
||||||
HRSContentView(state = HRSServiceData(), zoomIn = false) { }
|
HRSContentView(state = HRSServiceData()) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.DeviceDisconnectedView
|
||||||
import no.nordicsemi.android.common.ui.scanner.view.Reason
|
import no.nordicsemi.android.common.ui.scanner.view.Reason
|
||||||
import no.nordicsemi.android.hrs.R
|
import no.nordicsemi.android.hrs.R
|
||||||
|
import no.nordicsemi.android.hrs.data.HRSServiceData
|
||||||
import no.nordicsemi.android.hrs.viewmodel.HRSViewModel
|
import no.nordicsemi.android.hrs.viewmodel.HRSViewModel
|
||||||
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
|
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
|
||||||
import no.nordicsemi.android.ui.view.BackIconAppBar
|
import no.nordicsemi.android.ui.view.BackIconAppBar
|
||||||
@@ -70,14 +71,15 @@ fun HRSScreen() {
|
|||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
when (state.hrsManagerState) {
|
if (state.deviceName == null) {
|
||||||
NoDeviceState -> DeviceConnectingView()
|
DeviceConnectingView()
|
||||||
is WorkingState -> when (state.hrsManagerState.result.connectionState) {
|
} else {
|
||||||
|
when (state.connectionState) {
|
||||||
null,
|
null,
|
||||||
GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) }
|
GattConnectionState.STATE_CONNECTING -> DeviceConnectingView { NavigateUpButton(navigateUp) }
|
||||||
GattConnectionState.STATE_DISCONNECTED,
|
GattConnectionState.STATE_DISCONNECTED,
|
||||||
GattConnectionState.STATE_DISCONNECTING -> DeviceDisconnectedView(Reason.UNKNOWN) { NavigateUpButton(navigateUp) }
|
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
|
@Composable
|
||||||
private fun AppBar(state: HRSViewState, navigateUp: () -> Unit, viewModel: HRSViewModel) {
|
private fun AppBar(state: HRSServiceData, navigateUp: () -> Unit, viewModel: HRSViewModel) {
|
||||||
if (state.deviceName?.isNotBlank() == true) {
|
if (state.deviceName?.isNotBlank() == true) {
|
||||||
LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) {
|
LoggerIconAppBar(state.deviceName, navigateUp, { viewModel.onEvent(DisconnectEvent) }) {
|
||||||
viewModel.onEvent(OpenLoggerEvent)
|
viewModel.onEvent(OpenLoggerEvent)
|
||||||
|
|||||||
@@ -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()
|
|
||||||
@@ -35,8 +35,6 @@ import android.os.ParcelUuid
|
|||||||
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
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
import kotlinx.coroutines.flow.firstOrNull
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
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.service.HRS_SERVICE_UUID
|
||||||
import no.nordicsemi.android.hrs.view.DisconnectEvent
|
import no.nordicsemi.android.hrs.view.DisconnectEvent
|
||||||
import no.nordicsemi.android.hrs.view.HRSScreenViewEvent
|
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.NavigateUpEvent
|
||||||
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.kotlin.ble.core.ServerDevice
|
import no.nordicsemi.android.kotlin.ble.core.ServerDevice
|
||||||
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
|
import no.nordicsemi.android.kotlin.ble.core.data.GattConnectionState
|
||||||
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
import no.nordicsemi.android.toolbox.scanner.ScannerDestinationId
|
||||||
@@ -67,8 +63,7 @@ internal class HRSViewModel @Inject constructor(
|
|||||||
private val analytics: AppAnalytics
|
private val analytics: AppAnalytics
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _state = MutableStateFlow(HRSViewState())
|
val state = repository.data
|
||||||
val state = _state.asStateFlow()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -78,8 +73,6 @@ internal class HRSViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
repository.data.onEach {
|
repository.data.onEach {
|
||||||
_state.value = _state.value.copy(hrsManagerState = WorkingState(it))
|
|
||||||
|
|
||||||
if (it.connectionState == GattConnectionState.STATE_CONNECTED) {
|
if (it.connectionState == GattConnectionState.STATE_CONNECTED) {
|
||||||
analytics.logEvent(ProfileConnectedEvent(Profile.HRS))
|
analytics.logEvent(ProfileConnectedEvent(Profile.HRS))
|
||||||
}
|
}
|
||||||
@@ -102,7 +95,6 @@ internal class HRSViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onDeviceSelected(device: ServerDevice) {
|
private fun onDeviceSelected(device: ServerDevice) {
|
||||||
_state.value = _state.value.copy(deviceName = device.name)
|
|
||||||
repository.launch(device)
|
repository.launch(device)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +108,7 @@ internal class HRSViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onZoomButtonClicked() {
|
private fun onZoomButtonClicked() {
|
||||||
_state.value = _state.value.copy(zoomIn = !_state.value.zoomIn)
|
repository.switchZoomIn()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun disconnect() {
|
private fun disconnect() {
|
||||||
|
|||||||
Reference in New Issue
Block a user