Implement screens for DFU

This commit is contained in:
Sylwester Zieliński
2022-01-06 12:47:37 +01:00
parent c567836d01
commit c57a9c0c98
56 changed files with 384 additions and 222 deletions

View File

@@ -279,11 +279,13 @@ fun HomeView(callback: (NavDestination) -> Unit) {
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
FeatureButton( FeatureButton(
R.drawable.ic_uart, R.string.uart_module, R.drawable.ic_dfu, R.string.dfu_module,
R.string.uart_module_full R.string.uart_module_full
) { callback(NavDestination.DFU) } ) { callback(NavDestination.DFU) }
} }
} }
Spacer(modifier = Modifier.height(16.dp))
} }
} }
} }

View File

@@ -12,6 +12,6 @@ class HomeViewModel @Inject constructor(
) : ViewModel() { ) : ViewModel() {
fun onDeviceSelected(device: DiscoveredBluetoothDevice) { fun onDeviceSelected(device: DiscoveredBluetoothDevice) {
deviceHolder.attachDevice(device.device) deviceHolder.attachDevice(device)
} }
} }

View File

@@ -6,6 +6,7 @@ dependencies {
implementation libs.nordic.ble.common implementation libs.nordic.ble.common
implementation libs.nordic.log implementation libs.nordic.log
implementation libs.nordic.ui.scanner
implementation libs.lifecycle.service implementation libs.lifecycle.service
implementation libs.localbroadcastmanager implementation libs.localbroadcastmanager

View File

@@ -54,7 +54,7 @@ abstract class BleProfileService : LifecycleService() {
* @return bluetooth device * @return bluetooth device
*/ */
private val bluetoothDevice: BluetoothDevice by lazy { private val bluetoothDevice: BluetoothDevice by lazy {
bluetoothDeviceHolder.device ?: throw IllegalArgumentException( bluetoothDeviceHolder.device?.device ?: throw IllegalArgumentException(
"No device associated with the application." "No device associated with the application."
) )
} }

View File

@@ -1,24 +1,16 @@
package no.nordicsemi.android.service package no.nordicsemi.android.service
import android.bluetooth.BluetoothDevice import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class SelectedBluetoothDeviceHolder @Inject constructor() { class SelectedBluetoothDeviceHolder @Inject constructor() {
var device: BluetoothDevice? = null var device: DiscoveredBluetoothDevice? = null
private set private set
fun isBondingRequired(): Boolean { fun attachDevice(newDevice: DiscoveredBluetoothDevice) {
return device?.bondState == BluetoothDevice.BOND_NONE
}
fun bondDevice() {
device?.createBond()
}
fun attachDevice(newDevice: BluetoothDevice) {
device = newDevice device = newDevice
} }

View File

@@ -9,6 +9,7 @@ dependencies {
implementation libs.nordic.ble.common implementation libs.nordic.ble.common
implementation libs.nordic.log implementation libs.nordic.log
implementation libs.nordic.ui.scanner
implementation libs.bundles.compose implementation libs.bundles.compose
implementation libs.androidx.core implementation libs.androidx.core

View File

@@ -8,7 +8,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class BPSDataHolder @Inject constructor() { internal class BPSRepository @Inject constructor() {
private val _data = MutableStateFlow(BPSData()) private val _data = MutableStateFlow(BPSData())
val data: StateFlow<BPSData> = _data val data: StateFlow<BPSData> = _data

View File

@@ -31,7 +31,7 @@ import no.nordicsemi.android.ble.common.callback.bps.BloodPressureMeasurementDat
import no.nordicsemi.android.ble.common.callback.bps.IntermediateCuffPressureDataCallback import no.nordicsemi.android.ble.common.callback.bps.IntermediateCuffPressureDataCallback
import no.nordicsemi.android.ble.common.profile.bp.BloodPressureTypes import no.nordicsemi.android.ble.common.profile.bp.BloodPressureTypes
import no.nordicsemi.android.ble.data.Data import no.nordicsemi.android.ble.data.Data
import no.nordicsemi.android.bps.data.BPSDataHolder import no.nordicsemi.android.bps.data.BPSRepository
import no.nordicsemi.android.log.LogContract import no.nordicsemi.android.log.LogContract
import no.nordicsemi.android.service.BatteryManager import no.nordicsemi.android.service.BatteryManager
import java.util.* import java.util.*
@@ -50,7 +50,7 @@ private val ICP_CHARACTERISTIC_UUID = UUID.fromString("00002A36-0000-1000-8000-0
@Singleton @Singleton
internal class BPSManager @Inject constructor( internal class BPSManager @Inject constructor(
@ApplicationContext context: Context, @ApplicationContext context: Context,
private val dataHolder: BPSDataHolder private val dataHolder: BPSRepository
) : BatteryManager(context) { ) : BatteryManager(context) {
private var bpmCharacteristic: BluetoothGattCharacteristic? = null private var bpmCharacteristic: BluetoothGattCharacteristic? = null

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.bps.viewmodel package no.nordicsemi.android.bps.viewmodel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.bps.data.BPSDataHolder import no.nordicsemi.android.bps.data.BPSRepository
import no.nordicsemi.android.bps.repository.BPSManager import no.nordicsemi.android.bps.repository.BPSManager
import no.nordicsemi.android.bps.view.BPSScreenViewEvent import no.nordicsemi.android.bps.view.BPSScreenViewEvent
import no.nordicsemi.android.bps.view.DisconnectEvent import no.nordicsemi.android.bps.view.DisconnectEvent
@@ -14,7 +14,7 @@ import javax.inject.Inject
internal class BPSViewModel @Inject constructor( internal class BPSViewModel @Inject constructor(
private val bpsManager: BPSManager, private val bpsManager: BPSManager,
private val deviceHolder: SelectedBluetoothDeviceHolder, private val deviceHolder: SelectedBluetoothDeviceHolder,
private val dataHolder: BPSDataHolder private val dataHolder: BPSRepository
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = dataHolder.data
@@ -27,7 +27,7 @@ internal class BPSViewModel @Inject constructor(
fun connectDevice() { fun connectDevice() {
deviceHolder.device?.let { deviceHolder.device?.let {
bpsManager.connect(it) bpsManager.connect(it.device)
.useAutoConnect(false) .useAutoConnect(false)
.retry(3, 100) .retry(3, 100)
.enqueue() .enqueue()

View File

@@ -6,7 +6,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class CGMDataHolder @Inject constructor() { internal class CGMRepository @Inject constructor() {
private val _data = MutableStateFlow(CGMData()) private val _data = MutableStateFlow(CGMData())
val data: StateFlow<CGMData> = _data.asStateFlow() val data: StateFlow<CGMData> = _data.asStateFlow()

View File

@@ -57,7 +57,7 @@ private val RACP_UUID = UUID.fromString("00002A52-0000-1000-8000-00805f9b34fb")
internal class CGMManager( internal class CGMManager(
context: Context, context: Context,
private val dataHolder: CGMDataHolder private val repository: CGMRepository
) : BatteryManager(context) { ) : BatteryManager(context) {
private var cgmStatusCharacteristic: BluetoothGattCharacteristic? = null private var cgmStatusCharacteristic: BluetoothGattCharacteristic? = null
@@ -83,7 +83,7 @@ internal class CGMManager(
private var sessionStartTime: Long = 0 private var sessionStartTime: Long = 0
override fun onBatteryLevelChanged(batteryLevel: Int) { override fun onBatteryLevelChanged(batteryLevel: Int) {
dataHolder.emitNewBatteryLevel(batteryLevel) repository.emitNewBatteryLevel(batteryLevel)
} }
override fun getGattCallback(): BatteryManagerGattCallback { override fun getGattCallback(): BatteryManagerGattCallback {
@@ -168,7 +168,7 @@ internal class CGMManager(
sessionStartTime + timeOffset * 60000L // Sequence number is in minutes since Start Session sessionStartTime + timeOffset * 60000L // Sequence number is in minutes since Start Session
val record = CGMRecord(timeOffset, glucoseConcentration, timestamp) val record = CGMRecord(timeOffset, glucoseConcentration, timestamp)
records.put(record.sequenceNumber, record) records.put(record.sequenceNumber, record)
dataHolder.emitNewRecords(records.toList()) repository.emitNewRecords(records.toList())
} }
override fun onContinuousGlucoseMeasurementReceivedWithCrcError( override fun onContinuousGlucoseMeasurementReceivedWithCrcError(
@@ -235,10 +235,10 @@ internal class CGMManager(
@RecordAccessControlPointCallback.RACPOpCode requestCode: Int @RecordAccessControlPointCallback.RACPOpCode requestCode: Int
) { ) {
when (requestCode) { when (requestCode) {
RecordAccessControlPointCallback.RACP_OP_CODE_ABORT_OPERATION -> dataHolder.setRequestStatus(RequestStatus.ABORTED) RecordAccessControlPointCallback.RACP_OP_CODE_ABORT_OPERATION -> repository.setRequestStatus(RequestStatus.ABORTED)
else -> { else -> {
recordAccessRequestInProgress = false recordAccessRequestInProgress = false
dataHolder.setRequestStatus(RequestStatus.SUCCESS) repository.setRequestStatus(RequestStatus.SUCCESS)
} }
} }
} }
@@ -248,7 +248,7 @@ internal class CGMManager(
@RecordAccessControlPointCallback.RACPOpCode requestCode: Int @RecordAccessControlPointCallback.RACPOpCode requestCode: Int
) { ) {
recordAccessRequestInProgress = false recordAccessRequestInProgress = false
dataHolder.setRequestStatus(RequestStatus.SUCCESS) repository.setRequestStatus(RequestStatus.SUCCESS)
} }
override fun onNumberOfRecordsReceived( override fun onNumberOfRecordsReceived(
@@ -274,7 +274,7 @@ internal class CGMManager(
} }
} else { } else {
recordAccessRequestInProgress = false recordAccessRequestInProgress = false
dataHolder.setRequestStatus(RequestStatus.SUCCESS) repository.setRequestStatus(RequestStatus.SUCCESS)
} }
} }
@@ -285,9 +285,9 @@ internal class CGMManager(
) { ) {
log(Log.WARN, "Record Access operation failed (error $errorCode)") log(Log.WARN, "Record Access operation failed (error $errorCode)")
if (errorCode == RecordAccessControlPointCallback.RACP_ERROR_OP_CODE_NOT_SUPPORTED) { if (errorCode == RecordAccessControlPointCallback.RACP_ERROR_OP_CODE_NOT_SUPPORTED) {
dataHolder.setRequestStatus(RequestStatus.NOT_SUPPORTED) repository.setRequestStatus(RequestStatus.NOT_SUPPORTED)
} else { } else {
dataHolder.setRequestStatus(RequestStatus.FAILED) repository.setRequestStatus(RequestStatus.FAILED)
} }
} }
}) })
@@ -382,7 +382,7 @@ internal class CGMManager(
fun requestLastRecord() { fun requestLastRecord() {
if (recordAccessControlPointCharacteristic == null) return if (recordAccessControlPointCharacteristic == null) return
clear() clear()
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
recordAccessRequestInProgress = true recordAccessRequestInProgress = true
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
@@ -398,7 +398,7 @@ internal class CGMManager(
fun requestFirstRecord() { fun requestFirstRecord() {
if (recordAccessControlPointCharacteristic == null) return if (recordAccessControlPointCharacteristic == null) return
clear() clear()
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
recordAccessRequestInProgress = true recordAccessRequestInProgress = true
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
@@ -426,7 +426,7 @@ internal class CGMManager(
fun requestAllRecords() { fun requestAllRecords() {
if (recordAccessControlPointCharacteristic == null) return if (recordAccessControlPointCharacteristic == null) return
clear() clear()
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
recordAccessRequestInProgress = true recordAccessRequestInProgress = true
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
@@ -445,7 +445,7 @@ internal class CGMManager(
if (records.size() == 0) { if (records.size() == 0) {
requestAllRecords() requestAllRecords()
} else { } else {
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
// Obtain the last sequence number // Obtain the last sequence number
val sequenceNumber = records.keyAt(records.size() - 1) + 1 val sequenceNumber = records.keyAt(records.size() - 1) + 1
@@ -468,7 +468,7 @@ internal class CGMManager(
fun deleteAllRecords() { fun deleteAllRecords() {
if (recordAccessControlPointCharacteristic == null) return if (recordAccessControlPointCharacteristic == null) return
clear() clear()
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
RecordAccessControlPointData.deleteAllStoredRecords() RecordAccessControlPointData.deleteAllStoredRecords()

View File

@@ -4,7 +4,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.cgms.data.CGMDataHolder import no.nordicsemi.android.cgms.data.CGMRepository
import no.nordicsemi.android.cgms.data.WorkingMode import no.nordicsemi.android.cgms.data.WorkingMode
import no.nordicsemi.android.service.ForegroundBleService import no.nordicsemi.android.service.ForegroundBleService
import no.nordicsemi.android.utils.exhaustive import no.nordicsemi.android.utils.exhaustive
@@ -14,7 +14,7 @@ import javax.inject.Inject
internal class CGMService : ForegroundBleService() { internal class CGMService : ForegroundBleService() {
@Inject @Inject
lateinit var dataHolder: CGMDataHolder lateinit var dataHolder: CGMRepository
override val manager: CGMManager by lazy { CGMManager(this, dataHolder) } override val manager: CGMManager by lazy { CGMManager(this, dataHolder) }

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.cgms.viewmodel package no.nordicsemi.android.cgms.viewmodel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.cgms.data.CGMDataHolder import no.nordicsemi.android.cgms.data.CGMRepository
import no.nordicsemi.android.cgms.view.CGMViewEvent import no.nordicsemi.android.cgms.view.CGMViewEvent
import no.nordicsemi.android.cgms.view.DisconnectEvent import no.nordicsemi.android.cgms.view.DisconnectEvent
import no.nordicsemi.android.cgms.view.OnWorkingModeSelected import no.nordicsemi.android.cgms.view.OnWorkingModeSelected
@@ -11,7 +11,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class CGMScreenViewModel @Inject constructor( internal class CGMScreenViewModel @Inject constructor(
private val dataHolder: CGMDataHolder private val dataHolder: CGMRepository
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = dataHolder.data

View File

@@ -7,7 +7,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class CSCDataHolder @Inject constructor() { internal class CSCRepository @Inject constructor() {
private val _data = MutableStateFlow(CSCData()) private val _data = MutableStateFlow(CSCData())
val data: StateFlow<CSCData> = _data val data: StateFlow<CSCData> = _data

View File

@@ -29,7 +29,7 @@ import android.util.Log
import androidx.annotation.FloatRange import androidx.annotation.FloatRange
import no.nordicsemi.android.ble.common.callback.csc.CyclingSpeedAndCadenceMeasurementDataCallback import no.nordicsemi.android.ble.common.callback.csc.CyclingSpeedAndCadenceMeasurementDataCallback
import no.nordicsemi.android.ble.data.Data import no.nordicsemi.android.ble.data.Data
import no.nordicsemi.android.csc.data.CSCDataHolder import no.nordicsemi.android.csc.data.CSCRepository
import no.nordicsemi.android.csc.repository.CSCMeasurementParser.parse import no.nordicsemi.android.csc.repository.CSCMeasurementParser.parse
import no.nordicsemi.android.csc.view.CSCSettings import no.nordicsemi.android.csc.view.CSCSettings
import no.nordicsemi.android.log.LogContract import no.nordicsemi.android.log.LogContract
@@ -42,7 +42,7 @@ val CYCLING_SPEED_AND_CADENCE_SERVICE_UUID: UUID = UUID.fromString("00001816-000
/** Cycling Speed and Cadence Measurement characteristic UUID. */ /** Cycling Speed and Cadence Measurement characteristic UUID. */
private val CSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A5B-0000-1000-8000-00805f9b34fb") private val CSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A5B-0000-1000-8000-00805f9b34fb")
internal class CSCManager(context: Context, private val dataHolder: CSCDataHolder) : BatteryManager(context) { internal class CSCManager(context: Context, private val dataHolder: CSCRepository) : BatteryManager(context) {
private var cscMeasurementCharacteristic: BluetoothGattCharacteristic? = null private var cscMeasurementCharacteristic: BluetoothGattCharacteristic? = null
private var wheelSize = CSCSettings.DefaultWheelSize.VALUE private var wheelSize = CSCSettings.DefaultWheelSize.VALUE

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.csc.repository package no.nordicsemi.android.csc.repository
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import no.nordicsemi.android.csc.data.CSCDataHolder import no.nordicsemi.android.csc.data.CSCRepository
import no.nordicsemi.android.service.ForegroundBleService import no.nordicsemi.android.service.ForegroundBleService
import javax.inject.Inject import javax.inject.Inject
@@ -9,7 +9,7 @@ import javax.inject.Inject
internal class CSCService : ForegroundBleService() { internal class CSCService : ForegroundBleService() {
@Inject @Inject
lateinit var dataHolder: CSCDataHolder lateinit var dataHolder: CSCRepository
override val manager: CSCManager by lazy { CSCManager(this, dataHolder) } override val manager: CSCManager by lazy { CSCManager(this, dataHolder) }
} }

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.csc.viewmodel package no.nordicsemi.android.csc.viewmodel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.csc.data.CSCDataHolder import no.nordicsemi.android.csc.data.CSCRepository
import no.nordicsemi.android.csc.view.CSCViewEvent import no.nordicsemi.android.csc.view.CSCViewEvent
import no.nordicsemi.android.csc.view.OnCloseSelectWheelSizeDialog import no.nordicsemi.android.csc.view.OnCloseSelectWheelSizeDialog
import no.nordicsemi.android.csc.view.OnDisconnectButtonClick import no.nordicsemi.android.csc.view.OnDisconnectButtonClick
@@ -14,7 +14,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class CSCViewModel @Inject constructor( internal class CSCViewModel @Inject constructor(
private val dataHolder: CSCDataHolder private val dataHolder: CSCRepository
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = dataHolder.data

View File

@@ -13,6 +13,7 @@ dependencies {
implementation libs.nordic.log implementation libs.nordic.log
implementation libs.nordic.dfu implementation libs.nordic.dfu
implementation libs.nordic.ui.scanner
implementation libs.bundles.compose implementation libs.bundles.compose
implementation libs.androidx.core implementation libs.androidx.core

View File

@@ -1,12 +1,17 @@
package no.nordicsemi.dfu.data package no.nordicsemi.dfu.data
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import java.io.File import java.io.File
internal sealed class DFUData internal sealed class DFUData
internal object NoFileSelectedState : DFUData() internal object NoFileSelectedState : DFUData()
internal data class FileReadyState(val file: File, val isUploading: Boolean) : DFUData() internal data class FileReadyState(
val file: File,
val device: DiscoveredBluetoothDevice,
val isUploading: Boolean = false
) : DFUData()
internal object UploadSuccessState : DFUData() internal object UploadSuccessState : DFUData()

View File

@@ -1,15 +0,0 @@
package no.nordicsemi.dfu.data
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class DFUDataHolder @Inject constructor() {
private val _data = MutableStateFlow(NoFileSelectedState)
val data: StateFlow<DFUData> = _data
}

View File

@@ -0,0 +1,26 @@
package no.nordicsemi.dfu.data
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class DFURepository @Inject constructor(
private val deviceHolder: SelectedBluetoothDeviceHolder
) {
private val _data = MutableStateFlow<DFUData>(NoFileSelectedState)
val data: StateFlow<DFUData> = _data
fun initFile(file: File) {
_data.value = FileReadyState(file, deviceHolder.device!!)
}
fun install() {
val state = _data.value as FileReadyState
_data.value = state.copy(isUploading = true)
}
}

View File

@@ -1,23 +1,29 @@
package no.nordicsemi.dfu.view package no.nordicsemi.dfu.view
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import no.nordicsemi.android.utils.exhaustive import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.dfu.data.* import no.nordicsemi.dfu.data.*
@Composable @Composable
internal fun DFUContentView(state: DFUData, onEvent: (DFUViewEvent) -> Unit) { internal fun DFUContentView(state: DFUData, onEvent: (DFUViewEvent) -> Unit) {
Box(modifier = Modifier.padding(16.dp)) {
when (state) { when (state) {
NoFileSelectedState -> DFUSelectFileView() NoFileSelectedState -> DFUSelectFileView(onEvent)
is FileReadyState -> FileReadyView(state, onEvent) is FileReadyState -> FileReadyView(state, onEvent)
UploadFailureState -> DFUErrorView(onEvent)
UploadSuccessState -> DFUSuccessView(onEvent) UploadSuccessState -> DFUSuccessView(onEvent)
UploadFailureState -> DFUErrorView(onEvent)
}.exhaustive }.exhaustive
}
} }
@Composable @Composable
private fun FileReadyView(state: FileReadyState, onEvent: (DFUViewEvent) -> Unit) { private fun FileReadyView(state: FileReadyState, onEvent: (DFUViewEvent) -> Unit) {
when (state.isUploading) { when (state.isUploading) {
true -> DFUInstallingView(onEvent) false -> DFUSummaryView(state, onEvent)
false -> DFUSummaryView(onEvent) true -> DFUInstallingView(state, onEvent)
}.exhaustive }.exhaustive
} }

View File

@@ -1,18 +1,31 @@
package no.nordicsemi.dfu.view package no.nordicsemi.dfu.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import no.nordicsemi.dfu.R import no.nordicsemi.dfu.R
@Composable @Composable
internal fun DFUErrorView(onEvent: (DFUViewEvent) -> Unit) { internal fun DFUErrorView(onEvent: (DFUViewEvent) -> Unit) {
Column { Column {
Icon(
painter = painterResource(id = R.drawable.ic_fail_circle),
contentDescription = stringResource(id = R.string.dfu_failure_icon_description)
)
Spacer(modifier = Modifier.padding(16.dp))
Button(onClick = { onEvent(OnPauseButtonClick) }) { Button(onClick = { onEvent(OnPauseButtonClick) }) {
Text(text = stringResource(id = R.string.dfu_done)) Text(text = stringResource(id = R.string.dfu_close))
} }
} }
} }

View File

@@ -1,15 +1,22 @@
package no.nordicsemi.dfu.view package no.nordicsemi.dfu.view
import android.content.Context
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.core.net.toUri
import no.nordicsemi.android.dfu.DfuServiceInitiator
import no.nordicsemi.android.material.you.CircularProgressIndicator import no.nordicsemi.android.material.you.CircularProgressIndicator
import no.nordicsemi.dfu.R import no.nordicsemi.dfu.R
import no.nordicsemi.dfu.data.FileReadyState
import no.nordicsemi.dfu.repository.DFUService
@Composable @Composable
internal fun DFUInstallingView(onEvent: (DFUViewEvent) -> Unit) { internal fun DFUInstallingView(state: FileReadyState, onEvent: (DFUViewEvent) -> Unit) {
Column { Column {
CircularProgressIndicator() CircularProgressIndicator()
@@ -24,4 +31,38 @@ internal fun DFUInstallingView(onEvent: (DFUViewEvent) -> Unit) {
Text(text = stringResource(id = R.string.dfu_stop)) Text(text = stringResource(id = R.string.dfu_stop))
} }
} }
val context = LocalContext.current
LaunchedEffect(state.isUploading) {
if (state.isUploading) {
install(context, state)
}
}
}
private fun install(context: Context, state: FileReadyState) {
val device = state.device
val fileName = state.file.name
val fileLength = state.file.length()
val starter = DfuServiceInitiator(device.address)
.setDeviceName(device.displayName())
// .setKeepBond(keepBond)
// .setForceDfu(forceDfu)
// .setPacketsReceiptNotificationsEnabled(enablePRNs)
// .setPacketsReceiptNotificationsValue(numberOfPackets)
// .setPrepareDataObjectDelay(400)
// .setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true)
// if (fileType == DfuService.TYPE_AUTO) {
starter.setZip(state.file.toUri(), state.file.path)
// if (scope != null) starter.setScope(scope)
// } else {
// starter.setBinOrHex(fileType, fileStreamUri, filePath)
// .setInitFile(initFileStreamUri, initFilePath)
// }
starter.start(context, DFUService::class.java)
} }

View File

@@ -1,38 +1,51 @@
package no.nordicsemi.dfu.view package no.nordicsemi.dfu.view
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Column import androidx.compose.material.icons.Icons
import androidx.compose.foundation.layout.Row import androidx.compose.material.icons.filled.Settings
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import no.nordicsemi.android.dfu.DfuBaseService import no.nordicsemi.android.dfu.DfuBaseService
import no.nordicsemi.android.utils.EMPTY import no.nordicsemi.android.theme.view.ScreenSection
import no.nordicsemi.android.theme.view.SectionTitle
import no.nordicsemi.dfu.R import no.nordicsemi.dfu.R
@Composable @Composable
internal fun DFUSelectFileView() { internal fun DFUSelectFileView(onEvent: (DFUViewEvent) -> Unit) {
ScreenSection {
SectionTitle(icon = Icons.Default.Settings, title = stringResource(id = R.string.dfu_choose_file))
Spacer(modifier = Modifier.padding(8.dp))
Text(
text = stringResource(id = R.string.dfu_choose_info),
style = MaterialTheme.typography.bodyMedium
)
Spacer(modifier = Modifier.padding(8.dp))
ButtonsRow(onEvent)
}
}
@Composable
private fun ButtonsRow(onEvent: (DFUViewEvent) -> Unit) {
val result = remember { mutableStateOf<Uri?>(null) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
result.value = it onEvent(OnFileSelected(it!!))
} }
Row { Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxWidth()
) {
Button(onClick = { launcher.launch(DfuBaseService.MIME_TYPE_ZIP) }) { Button(onClick = { launcher.launch(DfuBaseService.MIME_TYPE_ZIP) }) {
Text(text = stringResource(id = R.string.dfu_select_zip)) Text(text = stringResource(id = R.string.dfu_select_zip))
} }
@@ -42,62 +55,3 @@ internal fun DFUSelectFileView() {
} }
} }
} }
@Composable
fun ChooseFileMangerDialog(onDismiss: () -> Unit) {
val alias = remember { mutableStateOf(String.EMPTY) }
val command = remember { mutableStateOf(String.EMPTY) }
val context = LocalContext.current
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text(text = stringResource(id = R.string.dfu_macro_dialog_title))
},
text = {
Column {
Text(stringResource(id = R.string.dfu_macro_dialog_info))
FileManagerOption.values().forEach {
FileManagerItem(item = it) {
openFileMangerPlayStore(context, it.url)
}
}
}
},
confirmButton = {
TextButton(
onClick = { onDismiss() }
) {
Text(stringResource(id = R.string.dfu_macro_dialog_dismiss))
}
},
dismissButton = {
}
)
}
private fun openFileMangerPlayStore(context: Context, url: String) {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
@Composable
private fun FileManagerItem(item: FileManagerOption, onItemSelected: (FileManagerOption) -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable { onItemSelected(item) }
) {
Text(text = item.title)
}
}
enum class FileManagerOption(val title: String, val url: String) {
DRIVE("Drive", "market://details?id=com.google.android.apps.docs"),
FILE_MANAGER("File Manager", "market://details?id=com.rhmsoft.fm"),
TOTAL_COMMANDER("Total Commander", "market://details?id=com.ghisler.android.TotalCommander"),
OTHERS("Search for others", "market://search?q=file manager"),
}

View File

@@ -1,16 +1,30 @@
package no.nordicsemi.dfu.view package no.nordicsemi.dfu.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import no.nordicsemi.dfu.R import no.nordicsemi.dfu.R
@Composable @Composable
internal fun DFUSuccessView(onEvent: (DFUViewEvent) -> Unit) { internal fun DFUSuccessView(onEvent: (DFUViewEvent) -> Unit) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(
painter = painterResource(id = R.drawable.ic_success_circle),
contentDescription = stringResource(id = R.string.dfu_success_icon_description)
)
Spacer(modifier = Modifier.padding(16.dp))
Column {
Button(onClick = { onEvent(OnPauseButtonClick) }) { Button(onClick = { onEvent(OnPauseButtonClick) }) {
Text(text = stringResource(id = R.string.dfu_done)) Text(text = stringResource(id = R.string.dfu_done))
} }

View File

@@ -1,23 +1,93 @@
package no.nordicsemi.dfu.view package no.nordicsemi.dfu.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import no.nordicsemi.android.material.you.CircularProgressIndicator import androidx.compose.ui.unit.dp
import no.nordicsemi.android.theme.view.ScreenSection
import no.nordicsemi.android.theme.view.SectionTitle
import no.nordicsemi.dfu.R import no.nordicsemi.dfu.R
import no.nordicsemi.dfu.data.FileReadyState
import no.nordicsemi.ui.scanner.DiscoveredBluetoothDevice
import java.io.File
@Composable @Composable
internal fun DFUSummaryView(onEvent: (DFUViewEvent) -> Unit) { internal fun DFUSummaryView(state: FileReadyState, onEvent: (DFUViewEvent) -> Unit) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
DeviceDetailsView(state.device)
Column { Spacer(modifier = Modifier.height(16.dp))
CircularProgressIndicator()
//todo add percentage indicator FileDetailsView(state.file)
Button(onClick = { onEvent(OnPauseButtonClick) }) { Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { onEvent(OnInstallButtonClick) }) {
Text(text = stringResource(id = R.string.dfu_install)) Text(text = stringResource(id = R.string.dfu_install))
} }
} }
} }
@Composable
internal fun DeviceDetailsView(device: DiscoveredBluetoothDevice) {
ScreenSection {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(id = R.drawable.ic_bluetooth),
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSecondary),
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.secondary,
shape = CircleShape
)
.padding(8.dp)
)
Spacer(modifier = Modifier.padding(8.dp))
Column(modifier = Modifier
.fillMaxWidth()
.weight(1f)) {
Text(
text = device.displayName(),
style = MaterialTheme.typography.titleMedium
)
Text(text = device.displayAddress(), style = MaterialTheme.typography.bodyMedium)
}
}
}
}
@Composable
private fun FileDetailsView(file: File) {
val fileName = file.name
val fileLength = file.length()
ScreenSection {
SectionTitle(icon = Icons.Default.Notifications, title = stringResource(id = R.string.dfu_file_details))
Spacer(modifier = Modifier.padding(16.dp))
Text(text = fileName)
Spacer(modifier = Modifier.padding(4.dp))
Text(text = stringResource(id = R.string.dfu_file_size, fileLength))
}
}

View File

@@ -1,8 +1,12 @@
package no.nordicsemi.dfu.view package no.nordicsemi.dfu.view
import android.net.Uri
internal sealed class DFUViewEvent internal sealed class DFUViewEvent
internal data class OnFileSelected(val uri: String) : DFUViewEvent() internal data class OnFileSelected(val uri: Uri) : DFUViewEvent()
internal object OnInstallButtonClick : DFUViewEvent()
internal object OnPauseButtonClick : DFUViewEvent() internal object OnPauseButtonClick : DFUViewEvent()

View File

@@ -1,19 +1,32 @@
package no.nordicsemi.dfu.viewmodel package no.nordicsemi.dfu.viewmodel
import android.net.Uri
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.theme.viewmodel.CloseableViewModel import no.nordicsemi.android.theme.viewmodel.CloseableViewModel
import no.nordicsemi.dfu.data.DFUDataHolder import no.nordicsemi.android.utils.exhaustive
import no.nordicsemi.dfu.view.DFUViewEvent import no.nordicsemi.dfu.data.DFURepository
import no.nordicsemi.dfu.view.*
import java.io.File
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class DFUViewModel @Inject constructor( internal class DFUViewModel @Inject constructor(
private val dataHolder: DFUDataHolder private val repository: DFURepository,
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = repository.data
fun onEvent(event: DFUViewEvent) { fun onEvent(event: DFUViewEvent) {
when (event) {
OnDisconnectButtonClick -> finish()
is OnFileSelected -> repository.initFile(createFile(event.uri))
OnInstallButtonClick -> repository.install()
OnPauseButtonClick -> finish()
OnStopButtonClick -> finish()
}.exhaustive
}
private fun createFile(uri: Uri): File {
return File(requireNotNull(uri.path))
} }
} }

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="80dp"
android:height="80dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/nordicRed"
android:pathData="M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="80dp"
android:height="80dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/nordicGrass"
android:pathData="M12,2C6.5,2 2,6.5 2,12S6.5,22 12,22 22,17.5 22,12 17.5,2 12,2M10,17L5,12L6.41,10.59L10,14.17L17.59,6.58L19,8L10,17Z" />
</vector>

View File

@@ -3,6 +3,7 @@
<string name="dfu_title">DFU</string> <string name="dfu_title">DFU</string>
<string name="dfu_done">Done</string> <string name="dfu_done">Done</string>
<string name="dfu_close">Close</string>
<string name="dfu_pause">Pause</string> <string name="dfu_pause">Pause</string>
<string name="dfu_stop">Stop</string> <string name="dfu_stop">Stop</string>
<string name="dfu_install">Install</string> <string name="dfu_install">Install</string>
@@ -10,8 +11,22 @@
<string name="dfu_select_zip">Select .zip</string> <string name="dfu_select_zip">Select .zip</string>
<string name="dfu_select_hex">Select .hex</string> <string name="dfu_select_hex">Select .hex</string>
<string name="dfu_file_details">File details</string>
<string name="dfu_file_size">%d bytes</string>
<string name="dfu_file_type_zip">Distribution packet (ZIP)</string>
<string name="dfu_file_type_soft_device">Soft Device</string>
<string name="dfu_file_type_bootloader">Bootloader</string>
<string name="dfu_file_type_application">Application</string>
<string name="dfu_choose_file">Choose file</string>
<string name="dfu_choose_info">Please select .zip or .hex file with bootloader, application or soft device.</string>
<string name="dfu_macro_dialog_title">File managers</string> <string name="dfu_macro_dialog_title">File managers</string>
<string name="dfu_macro_dialog_info">Please select </string> <string name="dfu_macro_dialog_info">Please select </string>
<string name="dfu_macro_dialog_confirm">Confirm</string> <string name="dfu_macro_dialog_confirm">Confirm</string>
<string name="dfu_macro_dialog_dismiss">Dismiss</string> <string name="dfu_macro_dialog_dismiss">Dismiss</string>
<string name="dfu_success_icon_description">Operation success</string>
<string name="dfu_failure_icon_description">Operation failed</string>
</resources> </resources>

View File

@@ -10,6 +10,7 @@ dependencies {
implementation libs.nordic.ble.common implementation libs.nordic.ble.common
implementation libs.nordic.theme implementation libs.nordic.theme
implementation libs.nordic.ui.scanner
implementation libs.nordic.log implementation libs.nordic.log

View File

@@ -7,7 +7,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class GLSDataHolder @Inject constructor() { internal class GLSRepository @Inject constructor() {
private val _data = MutableStateFlow(GLSData()) private val _data = MutableStateFlow(GLSData())
val data: StateFlow<GLSData> = _data.asStateFlow() val data: StateFlow<GLSData> = _data.asStateFlow()

View File

@@ -70,7 +70,7 @@ private val RACP_CHARACTERISTIC = UUID.fromString("00002A52-0000-1000-8000-00805
@Singleton @Singleton
internal class GLSManager @Inject constructor( internal class GLSManager @Inject constructor(
@ApplicationContext context: Context, @ApplicationContext context: Context,
private val dataHolder: GLSDataHolder private val repository: GLSRepository
) : BatteryManager(context) { ) : BatteryManager(context) {
private var glucoseMeasurementCharacteristic: BluetoothGattCharacteristic? = null private var glucoseMeasurementCharacteristic: BluetoothGattCharacteristic? = null
@@ -78,7 +78,7 @@ internal class GLSManager @Inject constructor(
private var recordAccessControlPointCharacteristic: BluetoothGattCharacteristic? = null private var recordAccessControlPointCharacteristic: BluetoothGattCharacteristic? = null
override fun onBatteryLevelChanged(batteryLevel: Int) { override fun onBatteryLevelChanged(batteryLevel: Int) {
dataHolder.setNewBatteryLevel(batteryLevel) repository.setNewBatteryLevel(batteryLevel)
} }
override fun getGattCallback(): BatteryManagerGattCallback { override fun getGattCallback(): BatteryManagerGattCallback {
@@ -135,7 +135,7 @@ internal class GLSManager @Inject constructor(
status = status?.value ?: 0 status = status?.value ?: 0
) )
dataHolder.addNewRecord(record) repository.addNewRecord(record)
} }
}) })
setNotificationCallback(glucoseMeasurementContextCharacteristic) setNotificationCallback(glucoseMeasurementContextCharacteristic)
@@ -177,7 +177,7 @@ internal class GLSManager @Inject constructor(
HbA1c = HbA1c ?: 0f HbA1c = HbA1c ?: 0f
) )
dataHolder.addNewContext(context) repository.addNewContext(context)
} }
}) })
setIndicationCallback(recordAccessControlPointCharacteristic) setIndicationCallback(recordAccessControlPointCharacteristic)
@@ -192,14 +192,14 @@ internal class GLSManager @Inject constructor(
RACP_OP_CODE_ABORT_OPERATION -> RequestStatus.ABORTED RACP_OP_CODE_ABORT_OPERATION -> RequestStatus.ABORTED
else -> RequestStatus.SUCCESS else -> RequestStatus.SUCCESS
} }
dataHolder.setRequestStatus(status) repository.setRequestStatus(status)
} }
override fun onRecordAccessOperationCompletedWithNoRecordsFound( override fun onRecordAccessOperationCompletedWithNoRecordsFound(
device: BluetoothDevice, device: BluetoothDevice,
@RACPOpCode requestCode: Int @RACPOpCode requestCode: Int
) { ) {
dataHolder.setRequestStatus(RequestStatus.SUCCESS) repository.setRequestStatus(RequestStatus.SUCCESS)
} }
override fun onNumberOfRecordsReceived( override fun onNumberOfRecordsReceived(
@@ -207,8 +207,8 @@ internal class GLSManager @Inject constructor(
numberOfRecords: Int numberOfRecords: Int
) { ) {
if (numberOfRecords > 0) { if (numberOfRecords > 0) {
if (dataHolder.records().isNotEmpty()) { if (repository.records().isNotEmpty()) {
val sequenceNumber = dataHolder.records().last().sequenceNumber + 1 //TODO check if correct val sequenceNumber = repository.records().last().sequenceNumber + 1 //TODO check if correct
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo( RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(
@@ -224,7 +224,7 @@ internal class GLSManager @Inject constructor(
.enqueue() .enqueue()
} }
} }
dataHolder.setRequestStatus(RequestStatus.SUCCESS) repository.setRequestStatus(RequestStatus.SUCCESS)
} }
override fun onRecordAccessOperationError( override fun onRecordAccessOperationError(
@@ -234,9 +234,9 @@ internal class GLSManager @Inject constructor(
) { ) {
log(Log.WARN, "Record Access operation failed (error $errorCode)") log(Log.WARN, "Record Access operation failed (error $errorCode)")
if (errorCode == RACP_ERROR_OP_CODE_NOT_SUPPORTED) { if (errorCode == RACP_ERROR_OP_CODE_NOT_SUPPORTED) {
dataHolder.setRequestStatus(RequestStatus.NOT_SUPPORTED) repository.setRequestStatus(RequestStatus.NOT_SUPPORTED)
} else { } else {
dataHolder.setRequestStatus(RequestStatus.FAILED) repository.setRequestStatus(RequestStatus.FAILED)
} }
} }
}) })
@@ -284,10 +284,10 @@ internal class GLSManager @Inject constructor(
* Clears the records list locally. * Clears the records list locally.
*/ */
private fun clear() { private fun clear() {
dataHolder.clearRecords() repository.clearRecords()
val target = bluetoothDevice val target = bluetoothDevice
if (target != null) { if (target != null) {
dataHolder.setRequestStatus(RequestStatus.SUCCESS) repository.setRequestStatus(RequestStatus.SUCCESS)
} }
} }
@@ -300,7 +300,7 @@ internal class GLSManager @Inject constructor(
if (recordAccessControlPointCharacteristic == null) return if (recordAccessControlPointCharacteristic == null) return
val target = bluetoothDevice ?: return val target = bluetoothDevice ?: return
clear() clear()
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
RecordAccessControlPointData.reportLastStoredRecord() RecordAccessControlPointData.reportLastStoredRecord()
@@ -323,7 +323,7 @@ internal class GLSManager @Inject constructor(
if (recordAccessControlPointCharacteristic == null) return if (recordAccessControlPointCharacteristic == null) return
val target = bluetoothDevice ?: return val target = bluetoothDevice ?: return
clear() clear()
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
RecordAccessControlPointData.reportFirstStoredRecord() RecordAccessControlPointData.reportFirstStoredRecord()
@@ -347,7 +347,7 @@ internal class GLSManager @Inject constructor(
if (recordAccessControlPointCharacteristic == null) return if (recordAccessControlPointCharacteristic == null) return
val target = bluetoothDevice ?: return val target = bluetoothDevice ?: return
clear() clear()
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
RecordAccessControlPointData.reportNumberOfAllStoredRecords() RecordAccessControlPointData.reportNumberOfAllStoredRecords()
@@ -375,13 +375,13 @@ internal class GLSManager @Inject constructor(
fun refreshRecords() { fun refreshRecords() {
if (recordAccessControlPointCharacteristic == null) return if (recordAccessControlPointCharacteristic == null) return
val target = bluetoothDevice ?: return val target = bluetoothDevice ?: return
if (dataHolder.records().isEmpty()) { if (repository.records().isEmpty()) {
requestAllRecords() requestAllRecords()
} else { } else {
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
// obtain the last sequence number // obtain the last sequence number
val sequenceNumber = dataHolder.records().last().sequenceNumber + 1 //TODO check if correct val sequenceNumber = repository.records().last().sequenceNumber + 1 //TODO check if correct
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber) RecordAccessControlPointData.reportStoredRecordsGreaterThenOrEqualTo(sequenceNumber)
@@ -425,7 +425,7 @@ internal class GLSManager @Inject constructor(
if (recordAccessControlPointCharacteristic == null) return if (recordAccessControlPointCharacteristic == null) return
val target = bluetoothDevice ?: return val target = bluetoothDevice ?: return
clear() clear()
dataHolder.setRequestStatus(RequestStatus.PENDING) repository.setRequestStatus(RequestStatus.PENDING)
writeCharacteristic( writeCharacteristic(
recordAccessControlPointCharacteristic, recordAccessControlPointCharacteristic,
RecordAccessControlPointData.deleteAllStoredRecords() RecordAccessControlPointData.deleteAllStoredRecords()

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.gls.viewmodel package no.nordicsemi.android.gls.viewmodel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.gls.data.GLSDataHolder import no.nordicsemi.android.gls.data.GLSRepository
import no.nordicsemi.android.gls.data.WorkingMode import no.nordicsemi.android.gls.data.WorkingMode
import no.nordicsemi.android.gls.repository.GLSManager import no.nordicsemi.android.gls.repository.GLSManager
import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder import no.nordicsemi.android.service.SelectedBluetoothDeviceHolder
@@ -13,7 +13,7 @@ import javax.inject.Inject
internal class GLSViewModel @Inject constructor( internal class GLSViewModel @Inject constructor(
private val glsManager: GLSManager, private val glsManager: GLSManager,
private val deviceHolder: SelectedBluetoothDeviceHolder, private val deviceHolder: SelectedBluetoothDeviceHolder,
private val dataHolder: GLSDataHolder private val dataHolder: GLSRepository
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = dataHolder.data
@@ -27,7 +27,7 @@ internal class GLSViewModel @Inject constructor(
fun connectDevice() { fun connectDevice() {
deviceHolder.device?.let { deviceHolder.device?.let {
glsManager.connect(it) glsManager.connect(it.device)
.useAutoConnect(false) .useAutoConnect(false)
.retry(3, 100) .retry(3, 100)
.enqueue() .enqueue()

View File

@@ -6,7 +6,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class HRSDataHolder @Inject constructor() { internal class HRSRepository @Inject constructor() {
private val _data = MutableStateFlow(HRSData()) private val _data = MutableStateFlow(HRSData())
val data: StateFlow<HRSData> = _data val data: StateFlow<HRSData> = _data

View File

@@ -31,7 +31,7 @@ import no.nordicsemi.android.ble.common.callback.hr.BodySensorLocationDataCallba
import no.nordicsemi.android.ble.common.callback.hr.HeartRateMeasurementDataCallback import no.nordicsemi.android.ble.common.callback.hr.HeartRateMeasurementDataCallback
import no.nordicsemi.android.ble.common.profile.hr.BodySensorLocation import no.nordicsemi.android.ble.common.profile.hr.BodySensorLocation
import no.nordicsemi.android.ble.data.Data import no.nordicsemi.android.ble.data.Data
import no.nordicsemi.android.hrs.data.HRSDataHolder import no.nordicsemi.android.hrs.data.HRSRepository
import no.nordicsemi.android.log.LogContract import no.nordicsemi.android.log.LogContract
import no.nordicsemi.android.service.BatteryManager import no.nordicsemi.android.service.BatteryManager
import java.util.* import java.util.*
@@ -46,7 +46,7 @@ private val HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A
* All operations required to connect to device with BLE Heart Rate Service and reading * All operations required to connect to device with BLE Heart Rate Service and reading
* heart rate values are performed here. * heart rate values are performed here.
*/ */
internal class HRSManager(context: Context, private val dataHolder: HRSDataHolder) : BatteryManager(context) { internal class HRSManager(context: Context, private val dataHolder: HRSRepository) : BatteryManager(context) {
private var heartRateCharacteristic: BluetoothGattCharacteristic? = null private var heartRateCharacteristic: BluetoothGattCharacteristic? = null
private var bodySensorLocationCharacteristic: BluetoothGattCharacteristic? = null private var bodySensorLocationCharacteristic: BluetoothGattCharacteristic? = null

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.hrs.service package no.nordicsemi.android.hrs.service
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import no.nordicsemi.android.hrs.data.HRSDataHolder import no.nordicsemi.android.hrs.data.HRSRepository
import no.nordicsemi.android.service.ForegroundBleService import no.nordicsemi.android.service.ForegroundBleService
import javax.inject.Inject import javax.inject.Inject
@@ -9,7 +9,7 @@ import javax.inject.Inject
internal class HRSService : ForegroundBleService() { internal class HRSService : ForegroundBleService() {
@Inject @Inject
lateinit var dataHolder: HRSDataHolder lateinit var dataHolder: HRSRepository
override val manager: HRSManager by lazy { HRSManager(this, dataHolder) } override val manager: HRSManager by lazy { HRSManager(this, dataHolder) }
} }

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.hrs.viewmodel package no.nordicsemi.android.hrs.viewmodel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.hrs.data.HRSDataHolder import no.nordicsemi.android.hrs.data.HRSRepository
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.theme.viewmodel.CloseableViewModel import no.nordicsemi.android.theme.viewmodel.CloseableViewModel
@@ -9,7 +9,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class HRSViewModel @Inject constructor( internal class HRSViewModel @Inject constructor(
private val dataHolder: HRSDataHolder private val dataHolder: HRSRepository
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = dataHolder.data

View File

@@ -6,7 +6,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class HTSDataHolder @Inject constructor() { internal class HTSRepository @Inject constructor() {
private val _data = MutableStateFlow(HTSData()) private val _data = MutableStateFlow(HTSData())
val data: StateFlow<HTSData> = _data val data: StateFlow<HTSData> = _data

View File

@@ -29,7 +29,7 @@ import no.nordicsemi.android.ble.common.callback.ht.TemperatureMeasurementDataCa
import no.nordicsemi.android.ble.common.profile.ht.TemperatureType import no.nordicsemi.android.ble.common.profile.ht.TemperatureType
import no.nordicsemi.android.ble.common.profile.ht.TemperatureUnit import no.nordicsemi.android.ble.common.profile.ht.TemperatureUnit
import no.nordicsemi.android.ble.data.Data import no.nordicsemi.android.ble.data.Data
import no.nordicsemi.android.hts.data.HTSDataHolder import no.nordicsemi.android.hts.data.HTSRepository
import no.nordicsemi.android.log.LogContract import no.nordicsemi.android.log.LogContract
import no.nordicsemi.android.service.BatteryManager import no.nordicsemi.android.service.BatteryManager
import java.util.* import java.util.*
@@ -44,7 +44,7 @@ private val HT_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A1C-0000-
*/ */
internal class HTSManager internal constructor( internal class HTSManager internal constructor(
context: Context, context: Context,
private val dataHolder: HTSDataHolder private val dataHolder: HTSRepository
) : BatteryManager(context) { ) : BatteryManager(context) {
private var htCharacteristic: BluetoothGattCharacteristic? = null private var htCharacteristic: BluetoothGattCharacteristic? = null

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.hts.repository package no.nordicsemi.android.hts.repository
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import no.nordicsemi.android.hts.data.HTSDataHolder import no.nordicsemi.android.hts.data.HTSRepository
import no.nordicsemi.android.service.ForegroundBleService import no.nordicsemi.android.service.ForegroundBleService
import javax.inject.Inject import javax.inject.Inject
@@ -9,7 +9,7 @@ import javax.inject.Inject
internal class HTSService : ForegroundBleService() { internal class HTSService : ForegroundBleService() {
@Inject @Inject
lateinit var dataHolder: HTSDataHolder lateinit var dataHolder: HTSRepository
override val manager: HTSManager by lazy { HTSManager(this, dataHolder) } override val manager: HTSManager by lazy { HTSManager(this, dataHolder) }
} }

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.hts.viewmodel package no.nordicsemi.android.hts.viewmodel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.hts.data.HTSDataHolder import no.nordicsemi.android.hts.data.HTSRepository
import no.nordicsemi.android.hts.view.DisconnectEvent import no.nordicsemi.android.hts.view.DisconnectEvent
import no.nordicsemi.android.hts.view.HTSScreenViewEvent import no.nordicsemi.android.hts.view.HTSScreenViewEvent
import no.nordicsemi.android.hts.view.OnTemperatureUnitSelected import no.nordicsemi.android.hts.view.OnTemperatureUnitSelected
@@ -11,7 +11,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class HTSViewModel @Inject constructor( internal class HTSViewModel @Inject constructor(
private val dataHolder: HTSDataHolder private val dataHolder: HTSRepository
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = dataHolder.data

View File

@@ -9,7 +9,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class PRXDataHolder @Inject constructor() { internal class PRXRepository @Inject constructor() {
private val _data = MutableStateFlow(PRXData()) private val _data = MutableStateFlow(PRXData())
val data: StateFlow<PRXData> = _data val data: StateFlow<PRXData> = _data

View File

@@ -33,7 +33,7 @@ import no.nordicsemi.android.ble.common.data.alert.AlertLevelData
import no.nordicsemi.android.ble.data.Data import no.nordicsemi.android.ble.data.Data
import no.nordicsemi.android.ble.error.GattError import no.nordicsemi.android.ble.error.GattError
import no.nordicsemi.android.log.LogContract import no.nordicsemi.android.log.LogContract
import no.nordicsemi.android.prx.data.PRXDataHolder import no.nordicsemi.android.prx.data.PRXRepository
import no.nordicsemi.android.service.BatteryManager import no.nordicsemi.android.service.BatteryManager
import java.util.* import java.util.*
@@ -48,7 +48,7 @@ val ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002A06-0000-1000-8000-0
internal class PRXManager( internal class PRXManager(
context: Context, context: Context,
private val dataHolder: PRXDataHolder private val dataHolder: PRXRepository
) : BatteryManager(context) { ) : BatteryManager(context) {
// Client characteristics. // Client characteristics.

View File

@@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.prx.data.AlarmLevel import no.nordicsemi.android.prx.data.AlarmLevel
import no.nordicsemi.android.prx.data.DisableAlarm import no.nordicsemi.android.prx.data.DisableAlarm
import no.nordicsemi.android.prx.data.EnableAlarm import no.nordicsemi.android.prx.data.EnableAlarm
import no.nordicsemi.android.prx.data.PRXDataHolder import no.nordicsemi.android.prx.data.PRXRepository
import no.nordicsemi.android.service.ForegroundBleService import no.nordicsemi.android.service.ForegroundBleService
import no.nordicsemi.android.utils.exhaustive import no.nordicsemi.android.utils.exhaustive
import javax.inject.Inject import javax.inject.Inject
@@ -16,7 +16,7 @@ import javax.inject.Inject
internal class PRXService : ForegroundBleService() { internal class PRXService : ForegroundBleService() {
@Inject @Inject
lateinit var dataHolder: PRXDataHolder lateinit var dataHolder: PRXRepository
@Inject @Inject
lateinit var alarmHandler: AlarmHandler lateinit var alarmHandler: AlarmHandler

View File

@@ -3,7 +3,7 @@ package no.nordicsemi.android.prx.viewmodel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.prx.data.DisableAlarm import no.nordicsemi.android.prx.data.DisableAlarm
import no.nordicsemi.android.prx.data.EnableAlarm import no.nordicsemi.android.prx.data.EnableAlarm
import no.nordicsemi.android.prx.data.PRXDataHolder import no.nordicsemi.android.prx.data.PRXRepository
import no.nordicsemi.android.prx.view.DisconnectEvent import no.nordicsemi.android.prx.view.DisconnectEvent
import no.nordicsemi.android.prx.view.PRXScreenViewEvent import no.nordicsemi.android.prx.view.PRXScreenViewEvent
import no.nordicsemi.android.prx.view.TurnOffAlert import no.nordicsemi.android.prx.view.TurnOffAlert
@@ -14,7 +14,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class PRXViewModel @Inject constructor( internal class PRXViewModel @Inject constructor(
private val dataHolder: PRXDataHolder private val dataHolder: PRXRepository
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = dataHolder.data

View File

@@ -6,7 +6,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class RSCSDataHolder @Inject constructor() { internal class RSCSRepository @Inject constructor() {
private val _data = MutableStateFlow(RSCSData()) private val _data = MutableStateFlow(RSCSData())
val data: StateFlow<RSCSData> = _data val data: StateFlow<RSCSData> = _data

View File

@@ -28,7 +28,7 @@ import android.content.Context
import no.nordicsemi.android.ble.common.callback.rsc.RunningSpeedAndCadenceMeasurementDataCallback import no.nordicsemi.android.ble.common.callback.rsc.RunningSpeedAndCadenceMeasurementDataCallback
import no.nordicsemi.android.ble.data.Data import no.nordicsemi.android.ble.data.Data
import no.nordicsemi.android.log.LogContract import no.nordicsemi.android.log.LogContract
import no.nordicsemi.android.rscs.data.RSCSDataHolder import no.nordicsemi.android.rscs.data.RSCSRepository
import no.nordicsemi.android.service.BatteryManager import no.nordicsemi.android.service.BatteryManager
import java.util.* import java.util.*
@@ -40,7 +40,7 @@ private val RSC_MEASUREMENT_CHARACTERISTIC_UUID = UUID.fromString("00002A53-0000
internal class RSCSManager internal constructor( internal class RSCSManager internal constructor(
context: Context, context: Context,
private val dataHolder: RSCSDataHolder private val dataHolder: RSCSRepository
) : BatteryManager(context) { ) : BatteryManager(context) {
private var rscMeasurementCharacteristic: BluetoothGattCharacteristic? = null private var rscMeasurementCharacteristic: BluetoothGattCharacteristic? = null

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.rscs.service package no.nordicsemi.android.rscs.service
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import no.nordicsemi.android.rscs.data.RSCSDataHolder import no.nordicsemi.android.rscs.data.RSCSRepository
import no.nordicsemi.android.service.ForegroundBleService import no.nordicsemi.android.service.ForegroundBleService
import javax.inject.Inject import javax.inject.Inject
@@ -9,7 +9,7 @@ import javax.inject.Inject
internal class RSCSService : ForegroundBleService() { internal class RSCSService : ForegroundBleService() {
@Inject @Inject
lateinit var dataHolder: RSCSDataHolder lateinit var dataHolder: RSCSRepository
override val manager: RSCSManager by lazy { RSCSManager(this, dataHolder) } override val manager: RSCSManager by lazy { RSCSManager(this, dataHolder) }
} }

View File

@@ -1,7 +1,7 @@
package no.nordicsemi.android.rscs.viewmodel package no.nordicsemi.android.rscs.viewmodel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.rscs.data.RSCSDataHolder import no.nordicsemi.android.rscs.data.RSCSRepository
import no.nordicsemi.android.rscs.view.DisconnectEvent import no.nordicsemi.android.rscs.view.DisconnectEvent
import no.nordicsemi.android.rscs.view.RSCScreenViewEvent import no.nordicsemi.android.rscs.view.RSCScreenViewEvent
import no.nordicsemi.android.theme.viewmodel.CloseableViewModel import no.nordicsemi.android.theme.viewmodel.CloseableViewModel
@@ -10,7 +10,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class RSCSViewModel @Inject constructor( internal class RSCSViewModel @Inject constructor(
private val dataHolder: RSCSDataHolder private val dataHolder: RSCSRepository
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = dataHolder.data

View File

@@ -9,7 +9,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
internal class UARTDataHolder @Inject constructor() { internal class UARTRepository @Inject constructor() {
private val _data = MutableStateFlow(UARTData()) private val _data = MutableStateFlow(UARTData())
val data = _data.asStateFlow() val data = _data.asStateFlow()

View File

@@ -29,7 +29,7 @@ import android.text.TextUtils
import no.nordicsemi.android.ble.WriteRequest import no.nordicsemi.android.ble.WriteRequest
import no.nordicsemi.android.log.LogContract import no.nordicsemi.android.log.LogContract
import no.nordicsemi.android.service.BatteryManager import no.nordicsemi.android.service.BatteryManager
import no.nordicsemi.android.uart.data.UARTDataHolder import no.nordicsemi.android.uart.data.UARTRepository
import no.nordicsemi.android.utils.EMPTY import no.nordicsemi.android.utils.EMPTY
import java.util.* import java.util.*
@@ -42,7 +42,7 @@ private val UART_RX_CHARACTERISTIC_UUID = UUID.fromString("6E400002-B5A3-F393-E0
/** TX characteristic UUID */ /** TX characteristic UUID */
private val UART_TX_CHARACTERISTIC_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E") private val UART_TX_CHARACTERISTIC_UUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")
internal class UARTManager(context: Context, private val dataHolder: UARTDataHolder) : BatteryManager(context) { internal class UARTManager(context: Context, private val dataHolder: UARTRepository) : BatteryManager(context) {
private var rxCharacteristic: BluetoothGattCharacteristic? = null private var rxCharacteristic: BluetoothGattCharacteristic? = null
private var txCharacteristic: BluetoothGattCharacteristic? = null private var txCharacteristic: BluetoothGattCharacteristic? = null

View File

@@ -5,14 +5,14 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import no.nordicsemi.android.service.ForegroundBleService import no.nordicsemi.android.service.ForegroundBleService
import no.nordicsemi.android.uart.data.UARTDataHolder import no.nordicsemi.android.uart.data.UARTRepository
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
internal class UARTService : ForegroundBleService() { internal class UARTService : ForegroundBleService() {
@Inject @Inject
lateinit var dataHolder: UARTDataHolder lateinit var dataHolder: UARTRepository
override val manager: UARTManager by lazy { UARTManager(this, dataHolder) } override val manager: UARTManager by lazy { UARTManager(this, dataHolder) }

View File

@@ -2,7 +2,7 @@ package no.nordicsemi.android.uart.viewmodel
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import no.nordicsemi.android.theme.viewmodel.CloseableViewModel import no.nordicsemi.android.theme.viewmodel.CloseableViewModel
import no.nordicsemi.android.uart.data.UARTDataHolder import no.nordicsemi.android.uart.data.UARTRepository
import no.nordicsemi.android.uart.data.UARTServiceCommand import no.nordicsemi.android.uart.data.UARTServiceCommand
import no.nordicsemi.android.uart.view.* import no.nordicsemi.android.uart.view.*
import no.nordicsemi.android.utils.exhaustive import no.nordicsemi.android.utils.exhaustive
@@ -10,7 +10,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
internal class UARTViewModel @Inject constructor( internal class UARTViewModel @Inject constructor(
private val dataHolder: UARTDataHolder private val dataHolder: UARTRepository
) : CloseableViewModel() { ) : CloseableViewModel() {
val state = dataHolder.data val state = dataHolder.data