From 2a9b66c3574e52c82c61337c26cbfbb73b69b922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Mon, 17 Jan 2022 17:01:52 +0100 Subject: [PATCH] Remove CloseableViewModel --- .../theme/viewmodel/CloseableViewModel.kt | 13 --- .../android/bps/data/BPSRepository.kt | 9 ++ .../android/bps/repository/BPSManager.kt | 26 ------ .../BloodPressureMeasurementParser.kt | 82 ------------------- .../android/bps/repository/DateTimeParser.kt | 53 ------------ .../IntermediateCuffPressureParser.kt | 78 ------------------ .../nordicsemi/android/bps/view/BPSScreen.kt | 23 +++--- .../nordicsemi/android/bps/view/UARTState.kt | 14 ++++ .../android/bps/viewmodel/BPSViewModel.kt | 51 +++++++++--- .../android/csc/data/CSCRepository.kt | 10 +++ .../android/csc/data/CSCServiceCommand.kt | 7 ++ .../android/csc/repository/CSCManager.kt | 6 -- .../csc/repository/CSCMeasurementParser.kt | 69 ---------------- .../android/csc/repository/CSCService.kt | 20 ++++- .../android/csc/viewmodel/CSCViewModel.kt | 4 +- .../android/uart/viewmodel/UARTViewModel.kt | 1 - 16 files changed, 111 insertions(+), 355 deletions(-) delete mode 100644 lib_theme/src/main/java/no/nordicsemi/android/theme/viewmodel/CloseableViewModel.kt delete mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BloodPressureMeasurementParser.kt delete mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/repository/DateTimeParser.kt delete mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/repository/IntermediateCuffPressureParser.kt create mode 100644 profile_bps/src/main/java/no/nordicsemi/android/bps/view/UARTState.kt create mode 100644 profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceCommand.kt delete mode 100644 profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCMeasurementParser.kt diff --git a/lib_theme/src/main/java/no/nordicsemi/android/theme/viewmodel/CloseableViewModel.kt b/lib_theme/src/main/java/no/nordicsemi/android/theme/viewmodel/CloseableViewModel.kt deleted file mode 100644 index 2b8af830..00000000 --- a/lib_theme/src/main/java/no/nordicsemi/android/theme/viewmodel/CloseableViewModel.kt +++ /dev/null @@ -1,13 +0,0 @@ -package no.nordicsemi.android.theme.viewmodel - -import androidx.lifecycle.ViewModel -import kotlinx.coroutines.flow.MutableStateFlow - -abstract class CloseableViewModel : ViewModel() { - - var isActive = MutableStateFlow(true) - - protected fun finish() { - isActive.tryEmit(false) - } -} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSRepository.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSRepository.kt index 90980589..0441cf91 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSRepository.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/data/BPSRepository.kt @@ -2,7 +2,9 @@ package no.nordicsemi.android.bps.data import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import no.nordicsemi.android.ble.common.profile.bp.BloodPressureTypes +import no.nordicsemi.android.service.BleManagerStatus import java.util.* import javax.inject.Inject import javax.inject.Singleton @@ -13,6 +15,9 @@ internal class BPSRepository @Inject constructor() { private val _data = MutableStateFlow(BPSData()) val data: StateFlow = _data + private val _status = MutableStateFlow(BleManagerStatus.CONNECTING) + val status = _status.asStateFlow() + fun setIntermediateCuffPressure( cuffPressure: Float, unit: Int, @@ -60,4 +65,8 @@ internal class BPSRepository @Inject constructor() { fun clear() { _data.tryEmit(BPSData()) } + + fun setNewStatus(status: BleManagerStatus) { + _status.value = status + } } diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSManager.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSManager.kt index 25e464e5..bb63b469 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSManager.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BPSManager.kt @@ -58,17 +58,6 @@ internal class BPSManager @Inject constructor( private val intermediateCuffPressureCallback = object : IntermediateCuffPressureDataCallback() { - override fun onDataReceived(device: BluetoothDevice, data: Data) { - log( - LogContract.Log.Level.APPLICATION, - "\"" + IntermediateCuffPressureParser.parse(data) - .toString() + "\" received" - ) - - // Pass through received data - super.onDataReceived(device, data) - } - override fun onIntermediateCuffPressureReceived( device: BluetoothDevice, cuffPressure: Float, @@ -95,17 +84,6 @@ internal class BPSManager @Inject constructor( private val bloodPressureMeasurementDataCallback = object : BloodPressureMeasurementDataCallback() { - override fun onDataReceived(device: BluetoothDevice, data: Data) { - log( - LogContract.Log.Level.APPLICATION, - "\"" + BloodPressureMeasurementParser.parse(data) - .toString() + "\" received" - ) - - // Pass through received data - super.onDataReceived(device, data) - } - override fun onBloodPressureMeasurementReceived( device: BluetoothDevice, systolic: Float, @@ -138,10 +116,6 @@ internal class BPSManager @Inject constructor( dataHolder.setBatteryLevel(batteryLevel) } - /** - * BluetoothGatt callbacks for connection/disconnection, service discovery, - * receiving notification, etc. - */ private inner class BloodPressureManagerGattCallback : BatteryManagerGattCallback() { override fun initialize() { diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BloodPressureMeasurementParser.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BloodPressureMeasurementParser.kt deleted file mode 100644 index ff92a7ae..00000000 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/BloodPressureMeasurementParser.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2015, 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.bps.repository - -import no.nordicsemi.android.ble.data.Data -import no.nordicsemi.android.bps.repository.DateTimeParser.parse -import java.lang.StringBuilder - -object BloodPressureMeasurementParser { - - fun parse(data: Data): String { - val builder = StringBuilder() - - // first byte - flags - var offset = 0 - val flags = data.getIntValue(Data.FORMAT_UINT8, offset++)!! - val unitType = flags and 0x01 - val timestampPresent = flags and 0x02 > 0 - val pulseRatePresent = flags and 0x04 > 0 - val userIdPresent = flags and 0x08 > 0 - val statusPresent = flags and 0x10 > 0 - - // following bytes - systolic, diastolic and mean arterial pressure - val systolic = data.getFloatValue(Data.FORMAT_SFLOAT, offset)!! - val diastolic = data.getFloatValue(Data.FORMAT_SFLOAT, offset + 2)!! - val meanArterialPressure = data.getFloatValue(Data.FORMAT_SFLOAT, offset + 4)!! - val unit = if (unitType == 0) " mmHg" else " kPa" - offset += 6 - builder.append("Systolic: ").append(systolic).append(unit) - builder.append("\nDiastolic: ").append(diastolic).append(unit) - builder.append("\nMean AP: ").append(meanArterialPressure).append(unit) - - // parse timestamp if present - if (timestampPresent) { - builder.append("\nTimestamp: ").append(parse(data, offset)) - offset += 7 - } - - // parse pulse rate if present - if (pulseRatePresent) { - val pulseRate = data.getFloatValue(Data.FORMAT_SFLOAT, offset)!! - offset += 2 - builder.append("\nPulse: ").append(pulseRate).append(" bpm") - } - if (userIdPresent) { - val userId = data.getIntValue(Data.FORMAT_UINT8, offset)!! - offset += 1 - builder.append("\nUser ID: ").append(userId) - } - if (statusPresent) { - val status = data.getIntValue(Data.FORMAT_UINT16, offset)!! - // offset += 2; - if (status and 0x0001 > 0) builder.append("\nBody movement detected") - if (status and 0x0002 > 0) builder.append("\nCuff too lose") - if (status and 0x0004 > 0) builder.append("\nIrregular pulse detected") - if (status and 0x0018 == 0x0008) builder.append("\nPulse rate exceeds upper limit") - if (status and 0x0018 == 0x0010) builder.append("\nPulse rate is less than lower limit") - if (status and 0x0018 == 0x0018) builder.append("\nPulse rate range: Reserved for future use ") - if (status and 0x0020 > 0) builder.append("\nImproper measurement position") - } - return builder.toString() - } -} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/DateTimeParser.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/DateTimeParser.kt deleted file mode 100644 index 7e38af1f..00000000 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/DateTimeParser.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2015, 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.bps.repository - -import no.nordicsemi.android.ble.common.callback.DateTimeDataCallback -import no.nordicsemi.android.ble.data.Data -import java.util.* - -object DateTimeParser { - /** - * Parses the date and time info. - * - * @param data - * @return time in human readable format - */ - fun parse(data: Data): String { - return parse(data, 0) - } - - /** - * Parses the date and time info. This data has 7 bytes - * - * @param data - * @param offset - * offset to start reading the time - * @return time in human readable format - */ - /* package */ - @JvmStatic - fun parse(data: Data, offset: Int): String { - val calendar = DateTimeDataCallback.readDateTime(data, offset) - return String.format(Locale.US, "%1\$te %1\$tb %1\$tY, %1\$tH:%1\$tM:%1\$tS", calendar) - } -} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/IntermediateCuffPressureParser.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/IntermediateCuffPressureParser.kt deleted file mode 100644 index 6b9bbb24..00000000 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/repository/IntermediateCuffPressureParser.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2015, 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.bps.repository - -import no.nordicsemi.android.ble.data.Data -import no.nordicsemi.android.bps.repository.DateTimeParser.parse -import java.lang.StringBuilder - -object IntermediateCuffPressureParser { - - fun parse(data: Data): String { - val builder = StringBuilder() - - // first byte - flags - var offset = 0 - val flags = data.getIntValue(Data.FORMAT_UINT8, offset++)!! - val unitType = flags and 0x01 - val timestampPresent = flags and 0x02 > 0 - val pulseRatePresent = flags and 0x04 > 0 - val userIdPresent = flags and 0x08 > 0 - val statusPresent = flags and 0x10 > 0 - - // following bytes - pressure - val pressure = data.getFloatValue(Data.FORMAT_SFLOAT, offset)!! - val unit = if (unitType == 0) " mmHg" else " kPa" - offset += 6 - builder.append("Cuff pressure: ").append(pressure).append(unit) - - // parse timestamp if present - if (timestampPresent) { - builder.append("Timestamp: ").append(parse(data, offset)) - offset += 7 - } - - // parse pulse rate if present - if (pulseRatePresent) { - val pulseRate = data.getFloatValue(Data.FORMAT_SFLOAT, offset)!! - offset += 2 - builder.append("\nPulse: ").append(pulseRate).append(" bpm") - } - if (userIdPresent) { - val userId = data.getIntValue(Data.FORMAT_UINT8, offset)!! - offset += 1 - builder.append("\nUser ID: ").append(userId) - } - if (statusPresent) { - val status = data.getIntValue(Data.FORMAT_UINT16, offset)!! - // offset += 2; - if (status and 0x0001 > 0) builder.append("\nBody movement detected") - if (status and 0x0002 > 0) builder.append("\nCuff too lose") - if (status and 0x0004 > 0) builder.append("\nIrregular pulse detected") - if (status and 0x0018 == 0x0008) builder.append("\nPulse rate exceeds upper limit") - if (status and 0x0018 == 0x0010) builder.append("\nPulse rate is less than lower limit") - if (status and 0x0018 == 0x0018) builder.append("\nPulse rate range: Reserved for future use ") - if (status and 0x0020 > 0) builder.append("\nImproper measurement position") - } - return builder.toString() - } -} diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt index 648f139f..dbaf9e4f 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/BPSScreen.kt @@ -7,36 +7,37 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import no.nordicsemi.android.bps.R -import no.nordicsemi.android.bps.data.BPSData import no.nordicsemi.android.bps.viewmodel.BPSViewModel import no.nordicsemi.android.theme.view.BackIconAppBar +import no.nordicsemi.android.theme.view.DeviceConnectingView +import no.nordicsemi.android.utils.exhaustive @Composable fun BPSScreen(finishAction: () -> Unit) { val viewModel: BPSViewModel = hiltViewModel() val state = viewModel.state.collectAsState().value - val isScreenActive = viewModel.isActive.collectAsState().value - LaunchedEffect("connect") { - viewModel.connectDevice() - } - - LaunchedEffect(isScreenActive) { - if (!isScreenActive) { + LaunchedEffect(state.isActive) { + if (state.isActive) { + viewModel.connectDevice() + } else { finishAction() } } - BPSView(state) { viewModel.onEvent(it) } + BPSView(state.viewState) { viewModel.onEvent(it) } } @Composable -private fun BPSView(state: BPSData, onEvent: (BPSScreenViewEvent) -> Unit) { +private fun BPSView(state: BPSViewState, onEvent: (BPSScreenViewEvent) -> Unit) { Column { BackIconAppBar(stringResource(id = R.string.bps_title)) { onEvent(DisconnectEvent) } - BPSContentView(state) { onEvent(it) } + when (state) { + is DisplayDataState -> BPSContentView(state.data) { onEvent(it) } + LoadingState -> DeviceConnectingView() + }.exhaustive } } diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/view/UARTState.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/UARTState.kt new file mode 100644 index 00000000..fd7a6f55 --- /dev/null +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/view/UARTState.kt @@ -0,0 +1,14 @@ +package no.nordicsemi.android.bps.view + +import no.nordicsemi.android.bps.data.BPSData + +internal data class BPSState( + val viewState: BPSViewState, + val isActive: Boolean = true +) + +internal sealed class BPSViewState + +internal object LoadingState : BPSViewState() + +internal data class DisplayDataState(val data: BPSData) : BPSViewState() diff --git a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt index 2d02fb65..f933b272 100644 --- a/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt +++ b/profile_bps/src/main/java/no/nordicsemi/android/bps/viewmodel/BPSViewModel.kt @@ -1,12 +1,22 @@ package no.nordicsemi.android.bps.viewmodel +import android.bluetooth.BluetoothDevice +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn import no.nordicsemi.android.bps.data.BPSRepository import no.nordicsemi.android.bps.repository.BPSManager import no.nordicsemi.android.bps.view.BPSScreenViewEvent +import no.nordicsemi.android.bps.view.BPSState import no.nordicsemi.android.bps.view.DisconnectEvent +import no.nordicsemi.android.bps.view.DisplayDataState +import no.nordicsemi.android.bps.view.LoadingState +import no.nordicsemi.android.service.BleManagerStatus +import no.nordicsemi.android.service.ConnectionObserverAdapter import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder -import no.nordicsemi.android.theme.viewmodel.CloseableViewModel import no.nordicsemi.android.utils.exhaustive import javax.inject.Inject @@ -14,10 +24,30 @@ import javax.inject.Inject internal class BPSViewModel @Inject constructor( private val bpsManager: BPSManager, private val deviceHolder: SelectedBluetoothDeviceHolder, - private val dataHolder: BPSRepository -) : CloseableViewModel() { + private val repository: BPSRepository +) : ViewModel() { - val state = dataHolder.data + val state = repository.data.combine(repository.status) { data, status -> + when (status) { + BleManagerStatus.CONNECTING -> BPSState(LoadingState) + BleManagerStatus.OK -> BPSState(DisplayDataState(data)) + BleManagerStatus.DISCONNECTED -> BPSState(DisplayDataState(data), false) + } + }.stateIn(viewModelScope, SharingStarted.Lazily, BPSState(LoadingState)) + + init { + bpsManager.setConnectionObserver(object : ConnectionObserverAdapter() { + override fun onDeviceConnected(device: BluetoothDevice) { + super.onDeviceConnected(device) + repository.setNewStatus(BleManagerStatus.OK) + } + + override fun onDeviceDisconnected(device: BluetoothDevice, reason: Int) { + super.onDeviceDisconnected(device, reason) + repository.setNewStatus(BleManagerStatus.DISCONNECTED) + } + }) + } fun onEvent(event: BPSScreenViewEvent) { when (event) { @@ -26,17 +56,14 @@ internal class BPSViewModel @Inject constructor( } fun connectDevice() { - deviceHolder.device?.let { - bpsManager.connect(it.device) - .useAutoConnect(false) - .retry(3, 100) - .enqueue() - } + bpsManager.connect(deviceHolder.device!!.device) + .useAutoConnect(false) + .retry(3, 100) + .enqueue() } private fun onDisconnectButtonClick() { - finish() deviceHolder.forgetDevice() - dataHolder.clear() + bpsManager.disconnect().enqueue() } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCRepository.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCRepository.kt index 19da5d5c..4cbc4aac 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCRepository.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCRepository.kt @@ -1,7 +1,10 @@ package no.nordicsemi.android.csc.data +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import no.nordicsemi.android.csc.view.SpeedUnit import javax.inject.Inject import javax.inject.Singleton @@ -12,6 +15,9 @@ internal class CSCRepository @Inject constructor() { private val _data = MutableStateFlow(CSCData()) val data: StateFlow = _data + private val _command = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_LATEST) + val command = _command.asSharedFlow() + fun setWheelSize(wheelSize: Int, wheelSizeDisplay: String) { _data.tryEmit(_data.value.copy( wheelSize = wheelSize, @@ -44,6 +50,10 @@ internal class CSCRepository @Inject constructor() { _data.tryEmit(_data.value.copy(batteryLevel = batteryLevel)) } + fun sendNewServiceCommand(workingMode: CSCServiceCommand) { + _command.tryEmit(workingMode) + } + fun clear() { _data.tryEmit(CSCData()) } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceCommand.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceCommand.kt new file mode 100644 index 00000000..de0364ca --- /dev/null +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/data/CSCServiceCommand.kt @@ -0,0 +1,7 @@ +package no.nordicsemi.android.csc.data + +internal sealed class CSCServiceCommand + +internal data class SetWheelSizeCommand(val size: Int) : CSCServiceCommand() + +internal object DisconnectCommand : CSCServiceCommand() diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCManager.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCManager.kt index 6925ce3e..b4919787 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCManager.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCManager.kt @@ -70,12 +70,6 @@ internal class CSCManager(context: Context, private val dataHolder: CSCRepositor // CSC characteristic is required setNotificationCallback(cscMeasurementCharacteristic) .with(object : CyclingSpeedAndCadenceMeasurementDataCallback() { - override fun onDataReceived(device: BluetoothDevice, data: Data) { - log(LogContract.Log.Level.APPLICATION, "\"" + parse(data) + "\" received") - - // Pass through received data - super.onDataReceived(device, data) - } override fun getWheelCircumference(): Float { return wheelSize.toFloat() diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCMeasurementParser.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCMeasurementParser.kt deleted file mode 100644 index 6049d60a..00000000 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCMeasurementParser.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2015, 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.repository - -import no.nordicsemi.android.ble.data.Data - -object CSCMeasurementParser { - - private const val WHEEL_REV_DATA_PRESENT: Byte = 0x01 // 1 bit - private const val CRANK_REV_DATA_PRESENT: Byte = 0x02 // 1 bit - - @JvmStatic - fun parse(data: Data): String { - var offset = 0 - val flags = data.getByte(offset)!!.toInt() // 1 byte - offset += 1 - val wheelRevPresent = flags and WHEEL_REV_DATA_PRESENT.toInt() > 0 - val crankRevPreset = flags and CRANK_REV_DATA_PRESENT.toInt() > 0 - var wheelRevolutions = 0 - var lastWheelEventTime = 0 - if (wheelRevPresent) { - wheelRevolutions = data.getIntValue(Data.FORMAT_UINT32, offset)!! - offset += 4 - lastWheelEventTime = data.getIntValue(Data.FORMAT_UINT16, offset)!! // 1/1024 s - offset += 2 - } - var crankRevolutions = 0 - var lastCrankEventTime = 0 - if (crankRevPreset) { - crankRevolutions = data.getIntValue(Data.FORMAT_UINT16, offset)!! - offset += 2 - lastCrankEventTime = data.getIntValue(Data.FORMAT_UINT16, offset)!! - //offset += 2; - } - val builder = StringBuilder() - if (wheelRevPresent) { - builder.append("Wheel rev: ").append(wheelRevolutions).append(",\n") - builder.append("Last wheel event time: ").append(lastWheelEventTime).append(",\n") - } - if (crankRevPreset) { - builder.append("Crank rev: ").append(crankRevolutions).append(",\n") - builder.append("Last crank event time: ").append(lastCrankEventTime).append(",\n") - } - if (!wheelRevPresent && !crankRevPreset) { - builder.append("No wheel or crank data") - } - builder.setLength(builder.length - 2) - return builder.toString() - } -} diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt index b6cd61cd..83b85ae5 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/repository/CSCService.kt @@ -1,15 +1,31 @@ package no.nordicsemi.android.csc.repository import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import no.nordicsemi.android.csc.data.CSCRepository +import no.nordicsemi.android.csc.data.DisconnectCommand +import no.nordicsemi.android.csc.data.SetWheelSizeCommand import no.nordicsemi.android.service.ForegroundBleService +import no.nordicsemi.android.utils.exhaustive import javax.inject.Inject @AndroidEntryPoint internal class CSCService : ForegroundBleService() { @Inject - lateinit var dataHolder: CSCRepository + lateinit var repository: CSCRepository - override val manager: CSCManager by lazy { CSCManager(this, dataHolder) } + override val manager: CSCManager by lazy { CSCManager(this, repository) } + + override fun onCreate() { + super.onCreate() + + repository.command.onEach { + when (it) { + DisconnectCommand -> stopSelf() + is SetWheelSizeCommand -> manager.setWheelSize(it.size) + }.exhaustive + }.launchIn(scope) + } } diff --git a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt index ba7bf3b8..5be6d88f 100644 --- a/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt +++ b/profile_csc/src/main/java/no/nordicsemi/android/csc/viewmodel/CSCViewModel.kt @@ -1,5 +1,6 @@ package no.nordicsemi.android.csc.viewmodel +import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import no.nordicsemi.android.csc.data.CSCRepository import no.nordicsemi.android.csc.view.CSCViewEvent @@ -8,14 +9,13 @@ import no.nordicsemi.android.csc.view.OnDisconnectButtonClick import no.nordicsemi.android.csc.view.OnSelectedSpeedUnitSelected import no.nordicsemi.android.csc.view.OnShowEditWheelSizeDialogButtonClick import no.nordicsemi.android.csc.view.OnWheelSizeSelected -import no.nordicsemi.android.theme.viewmodel.CloseableViewModel import no.nordicsemi.android.utils.exhaustive import javax.inject.Inject @HiltViewModel internal class CSCViewModel @Inject constructor( private val dataHolder: CSCRepository -) : CloseableViewModel() { +) : ViewModel() { val state = dataHolder.data diff --git a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt index 137a3894..d2ba6ed4 100644 --- a/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt +++ b/profile_uart/src/main/java/no/nordicsemi/android/uart/viewmodel/UARTViewModel.kt @@ -7,7 +7,6 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import no.nordicsemi.android.service.BleManagerStatus -import no.nordicsemi.android.theme.viewmodel.CloseableViewModel import no.nordicsemi.android.uart.data.DisconnectCommand import no.nordicsemi.android.uart.data.SendTextCommand import no.nordicsemi.android.uart.data.UARTRepository