Remove CloseableViewModel

This commit is contained in:
Sylwester Zieliński
2022-01-17 17:01:52 +01:00
parent 4aa0a69256
commit 2a9b66c357
16 changed files with 111 additions and 355 deletions

View File

@@ -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)
}
}

View File

@@ -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<BPSData> = _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
}
}

View File

@@ -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() {

View File

@@ -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()
}
}

View File

@@ -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)
}
}

View File

@@ -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()
}
}

View File

@@ -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") {
LaunchedEffect(state.isActive) {
if (state.isActive) {
viewModel.connectDevice()
}
LaunchedEffect(isScreenActive) {
if (!isScreenActive) {
} 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
}
}

View File

@@ -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()

View File

@@ -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)
bpsManager.connect(deviceHolder.device!!.device)
.useAutoConnect(false)
.retry(3, 100)
.enqueue()
}
}
private fun onDisconnectButtonClick() {
finish()
deviceHolder.forgetDevice()
dataHolder.clear()
bpsManager.disconnect().enqueue()
}
}

View File

@@ -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<CSCData> = _data
private val _command = MutableSharedFlow<CSCServiceCommand>(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())
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()
}
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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