mirror of
https://github.com/aljazceru/Android-nRF-Toolbox.git
synced 2025-12-22 00:44:26 +01:00
Implement screens for DFU
This commit is contained in:
@@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -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) }
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|
||||||
val result = remember { mutableStateOf<Uri?>(null) }
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
|
|
||||||
result.value = it
|
Text(
|
||||||
|
text = stringResource(id = R.string.dfu_choose_info),
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.padding(8.dp))
|
||||||
|
|
||||||
|
ButtonsRow(onEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
@Composable
|
||||||
|
private fun ButtonsRow(onEvent: (DFUViewEvent) -> Unit) {
|
||||||
|
|
||||||
|
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
|
||||||
|
onEvent(OnFileSelected(it!!))
|
||||||
|
}
|
||||||
|
|
||||||
|
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"),
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
profile_dfu/src/main/res/drawable/ic_fail_circle.xml
Normal file
9
profile_dfu/src/main/res/drawable/ic_fail_circle.xml
Normal 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>
|
||||||
9
profile_dfu/src/main/res/drawable/ic_success_circle.xml
Normal file
9
profile_dfu/src/main/res/drawable/ic_success_circle.xml
Normal 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>
|
||||||
@@ -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>
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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) }
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user