Fix some bugs, improve ui
This commit is contained in:
parent
5e309d001c
commit
2a43916ecc
|
|
@ -76,8 +76,6 @@ dependencies {
|
||||||
|
|
||||||
implementation "com.google.accompanist:accompanist-permissions:0.26.3-beta"
|
implementation "com.google.accompanist:accompanist-permissions:0.26.3-beta"
|
||||||
|
|
||||||
implementation "com.chargemap.compose:numberpicker:1.0.3"
|
|
||||||
|
|
||||||
implementation "com.patrykandpatrick.vico:core:1.6.4"
|
implementation "com.patrykandpatrick.vico:core:1.6.4"
|
||||||
implementation "com.patrykandpatrick.vico:compose:1.6.4"
|
implementation "com.patrykandpatrick.vico:compose:1.6.4"
|
||||||
implementation "com.patrykandpatrick.vico:compose-m3:1.6.4"
|
implementation "com.patrykandpatrick.vico:compose-m3:1.6.4"
|
||||||
|
|
|
||||||
|
|
@ -81,10 +81,23 @@ fun BleInfoView(
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
title = "Заряд аккумулятора",
|
title = "Заряд батареи",
|
||||||
subtitle = "${bleInfo.batteryLevel} %"
|
subtitle = "${bleInfo.batteryLevel} %"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SpecDivider()
|
||||||
|
|
||||||
|
BleInfoItem(
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.NetworkCell,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
},
|
||||||
|
title = "Мощность сигнала",
|
||||||
|
subtitle = if(bleInfo.rssi != null) "${bleInfo.rssi } dBm" else "Нет сигнала"
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import llc.arma.ble.app.ui.common.ViewState
|
||||||
import llc.arma.ble.app.ui.model.BleView
|
import llc.arma.ble.app.ui.model.BleView
|
||||||
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
|
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
|
||||||
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
|
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
|
||||||
|
import llc.arma.ble.domain.common.BleException
|
||||||
import llc.arma.ble.domain.model.Ble
|
import llc.arma.ble.domain.model.Ble
|
||||||
import llc.arma.ble.domain.usecase.GetBleBySerial
|
import llc.arma.ble.domain.usecase.GetBleBySerial
|
||||||
|
|
||||||
|
|
@ -32,7 +33,7 @@ class ConnectionContract {
|
||||||
object Loading : State()
|
object Loading : State()
|
||||||
|
|
||||||
data class DisplayException(
|
data class DisplayException(
|
||||||
val exception: GetBleBySerial.GetBleException
|
val exception: BleException
|
||||||
) : State()
|
) : State()
|
||||||
|
|
||||||
data class Display(
|
data class Display(
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package llc.arma.ble.app.ui.screen.connection
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import llc.arma.ble.app.ui.common.BaseViewModel
|
import llc.arma.ble.app.ui.common.BaseViewModel
|
||||||
import llc.arma.ble.app.ui.mapper.BleMapper
|
import llc.arma.ble.app.ui.mapper.BleMapper
|
||||||
|
|
@ -106,11 +108,13 @@ class ConnectionViewModel @Inject constructor(
|
||||||
getBleBySerial(serial).fold(
|
getBleBySerial(serial).fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
|
|
||||||
setState {
|
it.onEach {
|
||||||
ConnectionContract.State.Display(
|
setState {
|
||||||
ble = it
|
ConnectionContract.State.Display(
|
||||||
)
|
ble = it
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
|
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
|
|
@ -27,6 +28,7 @@ fun Loading(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
|
strokeCap = StrokeCap.Round,
|
||||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,6 @@ class ThermometerContract {
|
||||||
val ble: Ble.Thermometer
|
val ble: Ble.Thermometer
|
||||||
) : Event()
|
) : Event()
|
||||||
|
|
||||||
data class OnTxChanged(val tx: Int) : Event()
|
|
||||||
|
|
||||||
object OnNavigateUpClicked : Event()
|
object OnNavigateUpClicked : Event()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,7 @@ fun ThermometerScreen(
|
||||||
when(state){
|
when(state){
|
||||||
is ThermometerContract.State.Display -> {
|
is ThermometerContract.State.Display -> {
|
||||||
DisplayState(
|
DisplayState(
|
||||||
|
origin = state.origin,
|
||||||
ble = state.thermometer,
|
ble = state.thermometer,
|
||||||
onEvent = {
|
onEvent = {
|
||||||
viewModel.setEvent(it)
|
viewModel.setEvent(it)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ class ThermometerViewModel @Inject constructor(
|
||||||
override fun handleEvents(event: ThermometerContract.Event) {
|
override fun handleEvents(event: ThermometerContract.Event) {
|
||||||
when(event){
|
when(event){
|
||||||
is ThermometerContract.Event.OnNavigateUpClicked -> reduce(viewState.value, event)
|
is ThermometerContract.Event.OnNavigateUpClicked -> reduce(viewState.value, event)
|
||||||
is ThermometerContract.Event.OnTxChanged -> reduce(viewState.value, event)
|
|
||||||
is ThermometerContract.Event.OnBleChanged -> reduce(viewState.value, event)
|
is ThermometerContract.Event.OnBleChanged -> reduce(viewState.value, event)
|
||||||
is ThermometerContract.Event.OnSaveIntervalChanged -> reduce(viewState.value, event)
|
is ThermometerContract.Event.OnSaveIntervalChanged -> reduce(viewState.value, event)
|
||||||
is ThermometerContract.Event.OnSaveIntervalEdit -> reduce(viewState.value, event)
|
is ThermometerContract.Event.OnSaveIntervalEdit -> reduce(viewState.value, event)
|
||||||
|
|
@ -46,24 +45,30 @@ class ThermometerViewModel @Inject constructor(
|
||||||
setEffect { ThermometerContract.Effect.Navigation.NavigateUp }
|
setEffect { ThermometerContract.Effect.Navigation.NavigateUp }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reduce(
|
|
||||||
state: ThermometerContract.State,
|
|
||||||
event: ThermometerContract.Event.OnTxChanged
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun reduce(
|
private fun reduce(
|
||||||
state: ThermometerContract.State,
|
state: ThermometerContract.State,
|
||||||
event: ThermometerContract.Event.OnBleChanged
|
event: ThermometerContract.Event.OnBleChanged
|
||||||
) {
|
) {
|
||||||
setState {
|
|
||||||
ThermometerContract.State.Display(
|
when(state){
|
||||||
origin = event.ble,
|
is ThermometerContract.State.Display -> setState {
|
||||||
thermometer = bleMapper.map(event.ble) as BleView.Thermometer,
|
state.copy(
|
||||||
writeState = null
|
origin = Ble.Thermometer(
|
||||||
)
|
info = event.ble.info,
|
||||||
|
state = state.origin.state,
|
||||||
|
thermometerState = state.origin.thermometerState
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is ThermometerContract.State.Loading -> setState {
|
||||||
|
ThermometerContract.State.Display(
|
||||||
|
origin = event.ble,
|
||||||
|
thermometer = bleMapper.map(event.ble) as BleView.Thermometer,
|
||||||
|
writeState = null
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reduce(
|
private fun reduce(
|
||||||
|
|
@ -188,25 +193,48 @@ class ThermometerViewModel @Inject constructor(
|
||||||
|
|
||||||
if(state is ThermometerContract.State.Display){
|
if(state is ThermometerContract.State.Display){
|
||||||
|
|
||||||
state.writeState?.let {
|
state.writeState?.let { request ->
|
||||||
|
|
||||||
if(it is ThermometerContract.State.Display.WriteState.DisplayPreview) {
|
if(request is ThermometerContract.State.Display.WriteState.DisplayPreview) {
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
state.copy(
|
state.copy(
|
||||||
writeState = ThermometerContract.State.Display.WriteState.Writing(it.writeRequest)
|
writeState = ThermometerContract.State.Display.WriteState.Writing(request.writeRequest)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeBle(state.thermometer.info.serial, it.writeRequest).fold(
|
writeBle(state.thermometer.info.serial, request.writeRequest).fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
setState {
|
|
||||||
state.copy(
|
val currentState = viewState.value
|
||||||
writeState = ThermometerContract.State.Display.WriteState.Success
|
|
||||||
|
if(currentState is ThermometerContract.State.Display) {
|
||||||
|
|
||||||
|
val newBleObject = Ble.Thermometer(
|
||||||
|
info = currentState.origin.info,
|
||||||
|
state = currentState.origin.state.copy(
|
||||||
|
tx = request.writeRequest.tx ?: state.origin.state.tx
|
||||||
|
),
|
||||||
|
thermometerState = currentState.origin.thermometerState.copy(
|
||||||
|
saveHistory = request.writeRequest.saveHistory
|
||||||
|
?: currentState.origin.thermometerState.saveHistory,
|
||||||
|
historyInterval = request.writeRequest.historyInterval
|
||||||
|
?: currentState.origin.thermometerState.historyInterval,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
setState {
|
||||||
|
currentState.copy(
|
||||||
|
origin = newBleObject,
|
||||||
|
thermometer = bleMapper.map(newBleObject) as BleView.Thermometer,
|
||||||
|
writeState = ThermometerContract.State.Display.WriteState.Success
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
setState {
|
setState {
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,12 @@ import androidx.compose.ui.unit.dp
|
||||||
import llc.arma.ble.app.ui.model.BleView
|
import llc.arma.ble.app.ui.model.BleView
|
||||||
import llc.arma.ble.app.ui.screen.BleInfoView
|
import llc.arma.ble.app.ui.screen.BleInfoView
|
||||||
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
|
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
|
||||||
|
import llc.arma.ble.domain.model.Ble
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DisplayState(
|
fun DisplayState(
|
||||||
onEvent: (ThermometerContract.Event) -> Unit,
|
onEvent: (ThermometerContract.Event) -> Unit,
|
||||||
|
origin: Ble.Thermometer,
|
||||||
ble: BleView.Thermometer
|
ble: BleView.Thermometer
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -40,7 +42,7 @@ fun DisplayState(
|
||||||
horizontal = 8.dp
|
horizontal = 8.dp
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
BleInfoView(bleInfo = ble.info)
|
BleInfoView(bleInfo = origin.info)
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
package llc.arma.ble.app.ui.screen.thermometer.view
|
package llc.arma.ble.app.ui.screen.thermometer.view
|
||||||
|
|
||||||
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material.icons.rounded.KeyboardArrowDown
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material.icons.rounded.KeyboardArrowUp
|
||||||
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.chargemap.compose.numberpicker.NumberPicker
|
|
||||||
import llc.arma.ble.app.ui.model.BleView
|
import llc.arma.ble.app.ui.model.BleView
|
||||||
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
|
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
|
||||||
|
|
||||||
|
|
@ -23,6 +24,22 @@ fun IntervalEdit(
|
||||||
mutableStateOf((state.thermometerState.historyInterval / 1000 / 60 / 60).toInt())
|
mutableStateOf((state.thermometerState.historyInterval / 1000 / 60 / 60).toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val maxInterval = 240
|
||||||
|
|
||||||
|
if(value > maxInterval){
|
||||||
|
value = maxInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
if(value < 1){
|
||||||
|
value = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
val maxHours = maxInterval
|
||||||
|
val maxDays = maxInterval / 24
|
||||||
|
|
||||||
|
val dayValue = value / 24
|
||||||
|
val hourValue = value - (24 * dayValue)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
|
@ -35,28 +52,36 @@ fun IntervalEdit(
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
NumberPicker(
|
NumberPicker(
|
||||||
dividersColor = MaterialTheme.colorScheme.primary,
|
range = -1..maxDays,
|
||||||
value = value,
|
value = dayValue,
|
||||||
onValueChange = {
|
onValueChanged = { value = (it * 24) + hourValue }
|
||||||
value = it
|
|
||||||
},
|
|
||||||
textStyle = MaterialTheme.typography.titleMedium,
|
|
||||||
range = 1..100
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
||||||
Text(
|
Text(text = "Дни")
|
||||||
text = "ч.",
|
|
||||||
style = MaterialTheme.typography.titleMedium
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
|
||||||
|
NumberPicker(
|
||||||
|
range = -1..maxHours,
|
||||||
|
value = hourValue,
|
||||||
|
onValueChanged = {
|
||||||
|
value = it + (dayValue * 24)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
||||||
|
Text(text = "Часы")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
@ -92,4 +117,85 @@ fun IntervalEdit(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NumberPicker(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
range: IntRange,
|
||||||
|
value: Int,
|
||||||
|
onValueChanged: (Int) -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
LaunchedEffect(range){
|
||||||
|
|
||||||
|
if(value > range.last){
|
||||||
|
|
||||||
|
onValueChanged(range.last)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(value < range.first){
|
||||||
|
|
||||||
|
onValueChanged(range.first)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
){
|
||||||
|
|
||||||
|
FilledIconButton(
|
||||||
|
onClick = {
|
||||||
|
if(value < range.last) onValueChanged(value + 1)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.KeyboardArrowUp,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(36.dp))
|
||||||
|
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = value,
|
||||||
|
transitionSpec = {
|
||||||
|
if (targetState > initialState) {
|
||||||
|
slideInVertically { height -> height } + fadeIn() with
|
||||||
|
slideOutVertically { height -> -height } + fadeOut()
|
||||||
|
} else {
|
||||||
|
slideInVertically { height -> -height } + fadeIn() with
|
||||||
|
slideOutVertically { height -> height } + fadeOut()
|
||||||
|
}.using(
|
||||||
|
SizeTransform(clip = false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { targetCount ->
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.displaySmall,
|
||||||
|
text = "$targetCount"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(36.dp))
|
||||||
|
|
||||||
|
FilledIconButton(
|
||||||
|
onClick = {
|
||||||
|
if(value > range.first) onValueChanged(value - 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.KeyboardArrowDown,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +278,8 @@ class TemperatureHistoryViewModel @Inject constructor(
|
||||||
private val getTemperatureHistoryBySerial: GetTemperatureHistoryBySerial
|
private val getTemperatureHistoryBySerial: GetTemperatureHistoryBySerial
|
||||||
) : BaseViewModel<TemperatureHistoryContract.State, TemperatureHistoryContract.Event, TemperatureHistoryContract.Effect>() {
|
) : BaseViewModel<TemperatureHistoryContract.State, TemperatureHistoryContract.Event, TemperatureHistoryContract.Effect>() {
|
||||||
|
|
||||||
|
private var lastSerial: String? = null
|
||||||
|
|
||||||
override fun setInitialState() = TemperatureHistoryContract.State.Display(
|
override fun setInitialState() = TemperatureHistoryContract.State.Display(
|
||||||
ProgressState.Indeterminate
|
ProgressState.Indeterminate
|
||||||
)
|
)
|
||||||
|
|
@ -297,7 +299,9 @@ class TemperatureHistoryViewModel @Inject constructor(
|
||||||
|
|
||||||
if(state is TemperatureHistoryContract.State.Display) {
|
if(state is TemperatureHistoryContract.State.Display) {
|
||||||
|
|
||||||
if(state.loadingHistoryState is ProgressState.Indeterminate) {
|
if(lastSerial != event.serial) {
|
||||||
|
|
||||||
|
lastSerial = event.serial
|
||||||
|
|
||||||
setState {
|
setState {
|
||||||
TemperatureHistoryContract.State.Display(ProgressState.Indeterminate)
|
TemperatureHistoryContract.State.Display(ProgressState.Indeterminate)
|
||||||
|
|
@ -323,6 +327,7 @@ class TemperatureHistoryViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reduce(
|
private fun reduce(
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -27,10 +28,8 @@ fun Write(
|
||||||
onEvent: (ThermometerContract.Event) -> Unit
|
onEvent: (ThermometerContract.Event) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.animateContentSize { initialValue, targetValue -> }
|
modifier = Modifier.animateContentSize()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
|
|
@ -44,69 +43,73 @@ fun Write(
|
||||||
when (state) {
|
when (state) {
|
||||||
is ThermometerContract.State.Display.WriteState.DisplayPreview -> {
|
is ThermometerContract.State.Display.WriteState.DisplayPreview -> {
|
||||||
|
|
||||||
state.writeRequest.tx?.let {
|
if(state.writeRequest.tx != null || state.writeRequest.saveHistory != null || state.writeRequest.historyInterval != null) {
|
||||||
Box(
|
|
||||||
modifier = Modifier.padding(
|
|
||||||
vertical = 0.dp,
|
|
||||||
horizontal = 8.dp
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
|
|
||||||
Row(
|
state.writeRequest.tx?.let {
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier.padding(
|
||||||
.clip(RoundedCornerShape(16.dp))
|
vertical = 0.dp,
|
||||||
.padding(8.dp)
|
horizontal = 8.dp
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Column(
|
Row(
|
||||||
modifier = Modifier.weight(1f)
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.padding(8.dp)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Text(
|
Column(
|
||||||
text = "Мощность"
|
modifier = Modifier.weight(1f)
|
||||||
)
|
) {
|
||||||
Text(
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
Text(
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
text = "Мощность"
|
||||||
text = "${it.localizedName} db"
|
)
|
||||||
)
|
Text(
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
text = "${it.localizedName} db"
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
state.writeRequest.saveHistory?.let {
|
state.writeRequest.saveHistory?.let {
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.padding(
|
modifier = Modifier.padding(
|
||||||
vertical = 0.dp,
|
vertical = 0.dp,
|
||||||
horizontal = 8.dp
|
horizontal = 8.dp
|
||||||
)
|
)
|
||||||
) {
|
|
||||||
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier
|
|
||||||
.clip(RoundedCornerShape(16.dp))
|
|
||||||
.padding(8.dp)
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Column(
|
Row(
|
||||||
modifier = Modifier.weight(1f)
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.padding(8.dp)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Text(
|
Column(
|
||||||
text = "Сохранять историю измерений"
|
modifier = Modifier.weight(1f)
|
||||||
)
|
) {
|
||||||
Text(
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
Text(
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
text = "Сохранять историю измерений"
|
||||||
text = "${it.localizedName}"
|
)
|
||||||
)
|
Text(
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
text = "${it.localizedName}"
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,36 +117,36 @@ fun Write(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
state.writeRequest.historyInterval?.let {
|
||||||
|
|
||||||
state.writeRequest.historyInterval?.let {
|
Box(
|
||||||
|
modifier = Modifier.padding(
|
||||||
Box(
|
vertical = 0.dp,
|
||||||
modifier = Modifier.padding(
|
horizontal = 8.dp
|
||||||
vertical = 0.dp,
|
)
|
||||||
horizontal = 8.dp
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier
|
|
||||||
.clip(RoundedCornerShape(16.dp))
|
|
||||||
.padding(8.dp)
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Column(
|
Row(
|
||||||
modifier = Modifier.weight(1f)
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.padding(8.dp)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Text(
|
Column(
|
||||||
text = "Интервал измерний"
|
modifier = Modifier.weight(1f)
|
||||||
)
|
) {
|
||||||
Text(
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
Text(
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
text = "Интервал измерний"
|
||||||
text = "${it / 1000 / 60 / 60} ч."
|
)
|
||||||
)
|
Text(
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
text = "${it / 1000 / 60 / 60} ч."
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,55 +154,92 @@ fun Write(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Surface(
|
||||||
|
shape = CircleShape,
|
||||||
|
color = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
onClick = {
|
||||||
|
onEvent(ThermometerContract.Event.OnWriteBle)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp)
|
||||||
|
.height(50.dp),
|
||||||
|
) {
|
||||||
|
|
||||||
Surface(
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(8.dp)
|
|
||||||
.height(50.dp),
|
|
||||||
shape = CircleShape,
|
|
||||||
color = MaterialTheme.colorScheme.primaryContainer,
|
|
||||||
onClick = {
|
|
||||||
onEvent(ThermometerContract.Event.OnWriteBle)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Text(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
color = MaterialTheme.colorScheme.background,
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
text = "Записать"
|
||||||
|
)
|
||||||
|
|
||||||
Text(
|
}
|
||||||
modifier = Modifier.align(Alignment.Center),
|
|
||||||
color = MaterialTheme.colorScheme.background,
|
|
||||||
style = MaterialTheme.typography.labelLarge,
|
|
||||||
text = "Записать"
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Surface(
|
||||||
|
shape = CircleShape,
|
||||||
|
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
onClick = {
|
||||||
|
onEvent(ThermometerContract.Event.OnHideWriteBlePreview)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp)
|
||||||
|
.height(50.dp),
|
||||||
|
) {
|
||||||
|
|
||||||
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
|
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
text = "Отменить"
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Surface(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(8.dp)
|
|
||||||
.height(50.dp),
|
|
||||||
shape = CircleShape,
|
|
||||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
onClick = {
|
|
||||||
onEvent(ThermometerContract.Event.OnHideWriteBlePreview)
|
|
||||||
}
|
}
|
||||||
) {
|
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
} else {
|
||||||
|
|
||||||
Text(
|
Spacer(modifier = Modifier.height(38.dp))
|
||||||
modifier = Modifier.align(Alignment.Center),
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
Text(
|
||||||
style = MaterialTheme.typography.labelLarge,
|
text = "Нет изменений",
|
||||||
text = "Отменить"
|
modifier = Modifier
|
||||||
)
|
.align(Alignment.CenterHorizontally)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(64.dp))
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp)
|
||||||
|
.height(50.dp),
|
||||||
|
shape = CircleShape,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
onClick = {
|
||||||
|
onEvent(ThermometerContract.Event.OnHideWriteBlePreview)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
|
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
text = "Ок"
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,6 +256,7 @@ fun Write(
|
||||||
Spacer(modifier = Modifier.height(28.dp))
|
Spacer(modifier = Modifier.height(28.dp))
|
||||||
|
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
|
strokeCap = StrokeCap.Round,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.CenterHorizontally)
|
.align(Alignment.CenterHorizontally)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import android.bluetooth.le.ScanResult
|
||||||
import android.bluetooth.le.ScanSettings
|
import android.bluetooth.le.ScanSettings
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.SystemClock
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
|
@ -183,73 +184,134 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
override suspend fun getBleBySerial(
|
override suspend fun getBleBySerial(
|
||||||
serial: String
|
serial: String
|
||||||
): Result<Ble, GetBleBySerial.GetBleException> = suspendCancellableCoroutine {
|
): Result<Flow<Ble>, BleException> {
|
||||||
|
|
||||||
deviceCache[serial]?.let { result ->
|
deviceCache[serial]?.let { result ->
|
||||||
|
|
||||||
if (checkPermission()) {
|
return when(result.info.type) {
|
||||||
|
BleInfo.Type.BEACON -> {
|
||||||
|
Result.success(
|
||||||
|
flow {
|
||||||
|
|
||||||
if (it.isActive) {
|
while (true) {
|
||||||
|
|
||||||
val info = result.info
|
deviceCache[serial]?.let { newResult ->
|
||||||
|
|
||||||
val state = Ble.BleState(
|
val state = Ble.BleState(
|
||||||
tx = when (result.scanRecord?.txPowerLevel) {
|
tx = when (result.scanRecord?.txPowerLevel) {
|
||||||
-40 -> Ble.BleState.TX.MINUS_40
|
-40 -> Ble.BleState.TX.MINUS_40
|
||||||
-20 -> Ble.BleState.TX.MINUS_20
|
-20 -> Ble.BleState.TX.MINUS_20
|
||||||
-16 -> Ble.BleState.TX.MINUS_16
|
-16 -> Ble.BleState.TX.MINUS_16
|
||||||
-12 -> Ble.BleState.TX.MINUS_12
|
-12 -> Ble.BleState.TX.MINUS_12
|
||||||
-8 -> Ble.BleState.TX.MINUS_8
|
-8 -> Ble.BleState.TX.MINUS_8
|
||||||
-4 -> Ble.BleState.TX.MINUS_4
|
-4 -> Ble.BleState.TX.MINUS_4
|
||||||
3 -> Ble.BleState.TX.PLUS_3
|
3 -> Ble.BleState.TX.PLUS_3
|
||||||
4 -> Ble.BleState.TX.PLUS_4
|
4 -> Ble.BleState.TX.PLUS_4
|
||||||
else -> Ble.BleState.TX.ZERO
|
else -> Ble.BleState.TX.ZERO
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
emit(
|
||||||
|
|
||||||
val resultValue = when (info.type) {
|
Ble.Beacon(
|
||||||
|
info = newResult.info.copy(
|
||||||
|
rssi = if((SystemClock.elapsedRealtimeNanos() - newResult.timestampNanos) > 15_000_000_000) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
newResult.rssi
|
||||||
|
}
|
||||||
|
|
||||||
BleInfo.Type.BEACON -> Ble.Beacon(
|
),
|
||||||
info = info,
|
state = state
|
||||||
state = state
|
)
|
||||||
)
|
|
||||||
|
|
||||||
BleInfo.Type.THERMOMETER -> {
|
)
|
||||||
|
|
||||||
val thermometer = readThermometerState(result).fold(
|
}
|
||||||
onFailure = { _ ->
|
|
||||||
return@launch it.resume(Result.failure(GetBleBySerial.GetBleException.BlePermissionDenied))
|
|
||||||
},
|
|
||||||
onSuccess = { return@fold it }
|
|
||||||
)
|
|
||||||
|
|
||||||
Ble.Thermometer(
|
delay(500)
|
||||||
info = info,
|
|
||||||
state = state,
|
|
||||||
thermometerState = thermometer
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
BleInfo.Type.THERMOMETER -> {
|
||||||
|
|
||||||
it.resume(Result.success(resultValue)) {}
|
val tState = suspendCancellableCoroutine {
|
||||||
|
|
||||||
}
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
|
||||||
|
it.resume(readThermometerState(result))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}.fold(
|
||||||
|
onFailure = {
|
||||||
|
return Result.failure(it)
|
||||||
|
},
|
||||||
|
onSuccess = {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Result.success(
|
||||||
|
flow {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
deviceCache[serial]?.let { newResult ->
|
||||||
|
|
||||||
|
val state = Ble.BleState(
|
||||||
|
tx = when (result.scanRecord?.txPowerLevel) {
|
||||||
|
-40 -> Ble.BleState.TX.MINUS_40
|
||||||
|
-20 -> Ble.BleState.TX.MINUS_20
|
||||||
|
-16 -> Ble.BleState.TX.MINUS_16
|
||||||
|
-12 -> Ble.BleState.TX.MINUS_12
|
||||||
|
-8 -> Ble.BleState.TX.MINUS_8
|
||||||
|
-4 -> Ble.BleState.TX.MINUS_4
|
||||||
|
3 -> Ble.BleState.TX.PLUS_3
|
||||||
|
4 -> Ble.BleState.TX.PLUS_4
|
||||||
|
else -> Ble.BleState.TX.ZERO
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
emit(
|
||||||
|
|
||||||
|
Ble.Thermometer(
|
||||||
|
info = newResult.info.copy(
|
||||||
|
rssi = if((SystemClock.elapsedRealtimeNanos() - newResult.timestampNanos) > 15_000_000_000) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
newResult.rssi
|
||||||
|
}
|
||||||
|
|
||||||
|
),
|
||||||
|
state = state,
|
||||||
|
thermometerState = tState
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(500)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
it.resume(Result.failure(GetBleBySerial.GetBleException.BlePermissionDenied)) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return llc.arma.ble.domain.Result.failure(BleException.UnexpectedResponse)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun readThermometerState(
|
private suspend fun readThermometerState(
|
||||||
|
|
@ -368,38 +430,74 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun writeBle(
|
override suspend fun writeBle(
|
||||||
serial: String,
|
serial: String,
|
||||||
request: Ble.Thermometer.WriteRequest
|
request: Ble.Thermometer.WriteRequest
|
||||||
): Result<Unit, BleException> {
|
): Result<Unit, BleException> = suspendCancellableCoroutine {
|
||||||
|
|
||||||
deviceCache[serial]?.let { result ->
|
deviceCache[serial]?.let { scanResult ->
|
||||||
|
|
||||||
request.tx?.let { writeTx(result.device, it) }?.onFailure {
|
if(checkPermission()) {
|
||||||
|
|
||||||
|
var gatt: BluetoothGatt? = null
|
||||||
|
|
||||||
|
val callback = WriteThermometerCallback(app, request) { result ->
|
||||||
|
|
||||||
|
gatt?.close()
|
||||||
|
|
||||||
|
result.onSuccess {
|
||||||
|
deviceCache.remove(serial)
|
||||||
|
resultList.remove(serial)
|
||||||
|
}
|
||||||
|
|
||||||
|
it.resume(result)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
gatt = scanResult.device.connectGatt(app, false, callback)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
it.resume(Result.failure(BleException.PermissionDenied))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*request.tx?.let {
|
||||||
|
Log.d("write", "tx")
|
||||||
|
writeTx(result.device, it)
|
||||||
|
}?.onFailure {
|
||||||
|
Log.d("write", "tx fail")
|
||||||
return Result.failure(it)
|
return Result.failure(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
request.historyInterval?.let { writeSaveInterval(result.device, it) }?.onFailure {
|
request.historyInterval?.let {
|
||||||
|
Log.d("write", "in")
|
||||||
|
writeSaveInterval(result.device, it)
|
||||||
|
}?.onFailure {
|
||||||
|
Log.d("write", "in fail")
|
||||||
return Result.failure(it)
|
return Result.failure(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
request.saveHistory?.let { writeSaveEnabled(result.device, it) }?.onFailure {
|
request.saveHistory?.let {
|
||||||
|
Log.d("write", "hs")
|
||||||
|
writeSaveEnabled(result.device, it)
|
||||||
|
}?.onFailure {
|
||||||
|
Log.d("write", "hs fail")
|
||||||
return Result.failure(it)
|
return Result.failure(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d("write", "fs")
|
||||||
|
|
||||||
writeToFlash(serial).onFailure {
|
writeToFlash(serial).onFailure {
|
||||||
|
Log.d("write", "fs fail")
|
||||||
return Result.failure(it)
|
return Result.failure(it)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
deviceCache.remove(serial)
|
|
||||||
resultList.remove(serial)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.success(Unit)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun writeBle(
|
override suspend fun writeBle(
|
||||||
|
|
@ -510,7 +608,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
serviceId = serviceUUID,
|
serviceId = serviceUUID,
|
||||||
characteristicId = intervalWriteUUID,
|
characteristicId = intervalWriteUUID,
|
||||||
writeData = mutableListOf<Byte>(3).apply {
|
writeData = mutableListOf<Byte>(3).apply {
|
||||||
addAll(interval.toUInt().to4ByteArrayInBigEndian().toList())
|
addAll((interval / 1_000).toUInt().to4ByteArrayInBigEndian().toList())
|
||||||
}.toByteArray()
|
}.toByteArray()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -699,7 +797,9 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
|
||||||
if (checkPermission()) {
|
if (checkPermission()) {
|
||||||
|
|
||||||
gatt.discoverServices()
|
gatt.discoverServices()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
it.resume(Result.failure(BleException.PermissionDenied))
|
it.resume(Result.failure(BleException.PermissionDenied))
|
||||||
|
|
@ -708,7 +808,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
it.resume(Result.failure(BleException.UnexpectedResponse))
|
it.resume(Result.success(Unit))
|
||||||
bleGatt?.close()
|
bleGatt?.close()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -752,10 +852,14 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d("write", "service not found")
|
||||||
|
|
||||||
|
gatt.disconnect()
|
||||||
it.resume(Result.failure(BleException.UnexpectedResponse))
|
it.resume(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
gatt.disconnect()
|
||||||
it.resume(Result.failure(BleException.UnexpectedResponse))
|
it.resume(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -787,6 +891,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
gatt.close()
|
||||||
it.resume(Result.failure(BleException.PermissionDenied))
|
it.resume(Result.failure(BleException.PermissionDenied))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package llc.arma.ble.data
|
package llc.arma.ble.data
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.bluetooth.BluetoothGatt
|
import android.bluetooth.BluetoothGatt
|
||||||
import android.bluetooth.BluetoothGattCallback
|
import android.bluetooth.BluetoothGattCallback
|
||||||
|
|
@ -13,7 +12,7 @@ import llc.arma.ble.domain.Result
|
||||||
import llc.arma.ble.domain.common.BleException
|
import llc.arma.ble.domain.common.BleException
|
||||||
import llc.arma.ble.domain.common.ProgressState
|
import llc.arma.ble.domain.common.ProgressState
|
||||||
import llc.arma.ble.domain.model.Ble
|
import llc.arma.ble.domain.model.Ble
|
||||||
import java.util.*
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
enum class Property {
|
enum class Property {
|
||||||
DATA_SIZE, PACKAGE
|
DATA_SIZE, PACKAGE
|
||||||
|
|
@ -24,12 +23,16 @@ class ReadHistoryCallback(
|
||||||
private val onResult: (Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>) -> Unit
|
private val onResult: (Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>) -> Unit
|
||||||
) : BluetoothGattCallback() {
|
) : BluetoothGattCallback() {
|
||||||
|
|
||||||
private fun ByteArray.getUIntAt(idx: Int) =
|
private fun ByteArray.get4byteUIntAt(idx: Int) =
|
||||||
((this[idx + 3].toUInt() and 0xFFu) shl 24) or
|
((this[idx + 3].toUInt() and 0xFFu) shl 24) or
|
||||||
((this[idx + 2].toUInt() and 0xFFu) shl 16) or
|
((this[idx + 2].toUInt() and 0xFFu) shl 16) or
|
||||||
((this[idx + 1].toUInt() and 0xFFu) shl 8) or
|
((this[idx + 1].toUInt() and 0xFFu) shl 8) or
|
||||||
(this[idx].toUInt() and 0xFFu)
|
(this[idx].toUInt() and 0xFFu)
|
||||||
|
|
||||||
|
private fun ByteArray.get2byteUIntAt(idx: Int) =
|
||||||
|
((this[idx + 1].toUInt() and 0xFFu) shl 8) or
|
||||||
|
(this[idx].toUInt() and 0xFFu)
|
||||||
|
|
||||||
private var readProperty: Property? = null
|
private var readProperty: Property? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
@ -122,6 +125,7 @@ class ReadHistoryCallback(
|
||||||
onCommonCharacteristicRead(gatt, characteristic, value, status)
|
onCommonCharacteristicRead(gatt, characteristic, value, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
private fun onCommonCharacteristicRead(
|
private fun onCommonCharacteristicRead(
|
||||||
gatt: BluetoothGatt,
|
gatt: BluetoothGatt,
|
||||||
characteristic: BluetoothGattCharacteristic,
|
characteristic: BluetoothGattCharacteristic,
|
||||||
|
|
@ -161,30 +165,29 @@ class ReadHistoryCallback(
|
||||||
|
|
||||||
if(value[0] == 250.toByte()){
|
if(value[0] == 250.toByte()){
|
||||||
|
|
||||||
bleMeasureInterval = value.getUIntAt(2).toLong()
|
bleMeasureInterval = value.get4byteUIntAt(4).toLong()
|
||||||
bleLastMeasureTime = value.getUIntAt(6).toLong()
|
bleLastMeasureTime = value.get4byteUIntAt(8).toLong()
|
||||||
bleRealTime = value.getUIntAt(10).toLong()
|
bleRealTime = value.get4byteUIntAt(12).toLong()
|
||||||
|
|
||||||
lastMeasureSystemTime = System.currentTimeMillis() - ((bleRealTime!! - bleLastMeasureTime!!) / 10_000)
|
lastMeasureSystemTime = System.currentTimeMillis() - ((bleRealTime!! - bleLastMeasureTime!!) * 1_000)
|
||||||
|
|
||||||
val temperatureDataArray = value.asList().subList(14, value.size)
|
val temperatureDataArray = value.toUByteArray().asList().subList(16, value.size)
|
||||||
|
|
||||||
resultTemperaturePackage.addAll(
|
resultTemperaturePackage.addAll(
|
||||||
temperatureDataArray.chunked(2).map {
|
temperatureDataArray.chunked(2).map {
|
||||||
(it[0] + it[1] * 256).toFloat() / 100f
|
(it[0] + it[1] * 256u).toFloat() / 100f
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
)
|
)
|
||||||
|
|
||||||
val totalDataSize = value[1].toUByte().toInt() + temperatureDataArray.size / 2
|
val totalDataSize = value.get2byteUIntAt(2).toInt() + temperatureDataArray.size / 2
|
||||||
|
|
||||||
val nextPackageDataCount = value[1].toUByte()
|
val nextPackageDataCount = value.get2byteUIntAt(2)
|
||||||
expectedDataSize = nextPackageDataCount.toInt() + resultTemperaturePackage.size
|
expectedDataSize = nextPackageDataCount.toInt() + resultTemperaturePackage.size
|
||||||
|
|
||||||
onResult(Result.success(ProgressState.Progress(0f / totalDataSize.toFloat())))
|
onResult(Result.success(ProgressState.Progress(0f / totalDataSize.toFloat())))
|
||||||
onResult(Result.success(ProgressState.Progress(nextPackageDataCount.toFloat() / totalDataSize.toFloat())))
|
onResult(Result.success(ProgressState.Progress(nextPackageDataCount.toFloat() / totalDataSize.toFloat())))
|
||||||
|
|
||||||
if(nextPackageDataCount != 0.toUByte()){
|
if(nextPackageDataCount != 0.toUInt()){
|
||||||
|
|
||||||
|
|
||||||
if (checkPermission()) {
|
if (checkPermission()) {
|
||||||
|
|
||||||
|
|
@ -218,18 +221,18 @@ class ReadHistoryCallback(
|
||||||
|
|
||||||
if (value[0] == 251.toByte()) {
|
if (value[0] == 251.toByte()) {
|
||||||
|
|
||||||
val nextPackageDataCount = value[1].toUByte()
|
val nextPackageDataCount = value.get2byteUIntAt(2)
|
||||||
val temperatureDataArray = value.toList().subList(2, value.size)
|
val temperatureDataArray = value.toUByteArray().toList().subList(4, value.size)
|
||||||
|
|
||||||
resultTemperaturePackage.addAll(
|
resultTemperaturePackage.addAll(
|
||||||
temperatureDataArray.chunked(2).map {
|
temperatureDataArray.chunked(2).map {
|
||||||
(it[0] + it[1] * 256).toFloat() / 100f
|
(it[0] + it[1] * 256u).toFloat() / 100f
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
onResult(Result.success(ProgressState.Progress(expectedDataSize!!.toFloat() / resultTemperaturePackage.size.toFloat())))
|
onResult(Result.success(ProgressState.Progress(expectedDataSize!!.toFloat() / resultTemperaturePackage.size.toFloat())))
|
||||||
|
|
||||||
if (nextPackageDataCount != 0.toUByte()) {
|
if (nextPackageDataCount != 0.toUInt()) {
|
||||||
|
|
||||||
val writeData = byteArrayOf(5)
|
val writeData = byteArrayOf(5)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,245 @@
|
||||||
|
package llc.arma.ble.data
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.Application
|
||||||
|
import android.bluetooth.BluetoothGatt
|
||||||
|
import android.bluetooth.BluetoothGattCallback
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic
|
||||||
|
import android.bluetooth.BluetoothProfile
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import llc.arma.ble.domain.Result
|
||||||
|
import llc.arma.ble.domain.common.BleException
|
||||||
|
import llc.arma.ble.domain.model.Ble
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class WriteThermometerCallback(
|
||||||
|
private val app: Application,
|
||||||
|
private var request: Ble.Thermometer.WriteRequest,
|
||||||
|
private val onResult: (Result<Unit, BleException>) -> Unit
|
||||||
|
) : BluetoothGattCallback() {
|
||||||
|
|
||||||
|
private var flashed = false
|
||||||
|
|
||||||
|
override fun onConnectionStateChange(
|
||||||
|
gatt: BluetoothGatt,
|
||||||
|
status: Int,
|
||||||
|
newState: Int
|
||||||
|
) {
|
||||||
|
super.onConnectionStateChange(gatt, status, newState)
|
||||||
|
|
||||||
|
Log.d("th", "onConnectionStateChange $status $newState")
|
||||||
|
|
||||||
|
if(checkPermission()) {
|
||||||
|
|
||||||
|
if(status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
|
||||||
|
gatt.discoverServices()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
onResult(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
onResult(Result.failure(BleException.PermissionDenied))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onServicesDiscovered(
|
||||||
|
gatt: BluetoothGatt,
|
||||||
|
status: Int
|
||||||
|
) {
|
||||||
|
Log.d("th", "onServicesDiscovered $status")
|
||||||
|
super.onServicesDiscovered(gatt, status)
|
||||||
|
onCycle(gatt, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onCycle(
|
||||||
|
gatt: BluetoothGatt,
|
||||||
|
status: Int
|
||||||
|
){
|
||||||
|
|
||||||
|
if(request.tx != null || request.saveHistory != null || request.historyInterval != null) {
|
||||||
|
|
||||||
|
fun UInt.to4ByteArrayInBigEndian(): ByteArray =
|
||||||
|
(3 downTo 0).map {
|
||||||
|
(this shr (it * Byte.SIZE_BITS)).toByte()
|
||||||
|
}.reversed().toByteArray()
|
||||||
|
|
||||||
|
var uuid: Pair<UUID, ByteArray>? = null
|
||||||
|
|
||||||
|
uuid = request.historyInterval?.let {
|
||||||
|
|
||||||
|
this.request = request.copy(
|
||||||
|
historyInterval = null
|
||||||
|
)
|
||||||
|
|
||||||
|
Pair(
|
||||||
|
intervalWriteUUID,
|
||||||
|
mutableListOf<Byte>(3).apply {
|
||||||
|
addAll((it).toUInt().to4ByteArrayInBigEndian().toList())
|
||||||
|
}.toByteArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
uuid = request.saveHistory?.let {
|
||||||
|
|
||||||
|
this.request = request.copy(
|
||||||
|
saveHistory = null
|
||||||
|
)
|
||||||
|
|
||||||
|
Pair(
|
||||||
|
saveEnabledWriteUUID,
|
||||||
|
mutableListOf<Byte>(4).apply {
|
||||||
|
add(if (it) 1 else 0)
|
||||||
|
}.toByteArray()
|
||||||
|
)
|
||||||
|
} ?: uuid
|
||||||
|
|
||||||
|
uuid = request.tx?.let {
|
||||||
|
|
||||||
|
this.request = request.copy(
|
||||||
|
tx = null
|
||||||
|
)
|
||||||
|
|
||||||
|
Pair(
|
||||||
|
txWriteUUID,
|
||||||
|
byteArrayOf(
|
||||||
|
when (it) {
|
||||||
|
Ble.BleState.TX.MINUS_40 -> -40
|
||||||
|
Ble.BleState.TX.MINUS_20 -> -20
|
||||||
|
Ble.BleState.TX.MINUS_16 -> -16
|
||||||
|
Ble.BleState.TX.MINUS_12 -> -12
|
||||||
|
Ble.BleState.TX.MINUS_8 -> -8
|
||||||
|
Ble.BleState.TX.MINUS_4 -> -4
|
||||||
|
Ble.BleState.TX.ZERO -> 0
|
||||||
|
Ble.BleState.TX.PLUS_3 -> 3
|
||||||
|
Ble.BleState.TX.PLUS_4 -> 4
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
} ?: uuid
|
||||||
|
|
||||||
|
uuid?.let { uuid ->
|
||||||
|
|
||||||
|
gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull {
|
||||||
|
it.uuid == uuid.first
|
||||||
|
}?.let {
|
||||||
|
|
||||||
|
gatt.writeCharacteristic(it, uuid.second)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onResult(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if(flashed.not()){
|
||||||
|
|
||||||
|
flashed = true
|
||||||
|
|
||||||
|
gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull {
|
||||||
|
it.uuid == flashWriteUUID
|
||||||
|
}?.let {
|
||||||
|
|
||||||
|
gatt.writeCharacteristic(it, byteArrayOf(9))
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onResult(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
onResult(Result.success(Unit))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCharacteristicWrite(
|
||||||
|
gatt: BluetoothGatt,
|
||||||
|
characteristic: BluetoothGattCharacteristic,
|
||||||
|
status: Int
|
||||||
|
) {
|
||||||
|
|
||||||
|
Log.d("th", "onCharacteristicWrite $status")
|
||||||
|
|
||||||
|
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||||
|
|
||||||
|
if(checkPermission()) {
|
||||||
|
|
||||||
|
if(status == BluetoothGatt.GATT_SUCCESS || flashed) {
|
||||||
|
|
||||||
|
onCycle(gatt, status)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
onResult(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
onResult(Result.failure(BleException.PermissionDenied))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BluetoothGatt.writeCharacteristic(
|
||||||
|
characteristic: BluetoothGattCharacteristic,
|
||||||
|
data: ByteArray
|
||||||
|
): Result<Unit, BleException> {
|
||||||
|
|
||||||
|
return if(checkPermission()){
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
|
||||||
|
}else{
|
||||||
|
|
||||||
|
characteristic.writeType
|
||||||
|
characteristic.value = data
|
||||||
|
writeCharacteristic(characteristic)
|
||||||
|
}
|
||||||
|
|
||||||
|
Result.success(Unit)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Result.failure(BleException.PermissionDenied)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkPermission(): Boolean {
|
||||||
|
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED &&
|
||||||
|
ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED
|
||||||
|
} else {
|
||||||
|
return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED &&
|
||||||
|
ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -26,13 +26,13 @@ sealed class Ble(
|
||||||
val value: Float
|
val value: Float
|
||||||
)
|
)
|
||||||
|
|
||||||
class ThermometerState(
|
data class ThermometerState(
|
||||||
val temperature: Float,
|
val temperature: Float,
|
||||||
val saveHistory: Boolean,
|
val saveHistory: Boolean,
|
||||||
val historyInterval: Long
|
val historyInterval: Long
|
||||||
)
|
)
|
||||||
|
|
||||||
class WriteRequest(
|
data class WriteRequest(
|
||||||
val tx: BleState.TX?,
|
val tx: BleState.TX?,
|
||||||
val saveHistory: Boolean?,
|
val saveHistory: Boolean?,
|
||||||
val historyInterval: Long?
|
val historyInterval: Long?
|
||||||
|
|
@ -40,7 +40,7 @@ sealed class Ble(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BleState(
|
data class BleState(
|
||||||
val tx: TX
|
val tx: TX
|
||||||
){
|
){
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@ package llc.arma.ble.domain.model
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
class BleInfo(
|
data class BleInfo(
|
||||||
val name: String,
|
val name: String,
|
||||||
val serial: String,
|
val serial: String,
|
||||||
val batteryLevel: Int,
|
val batteryLevel: Int,
|
||||||
val rssi: Int,
|
val rssi: Int?,
|
||||||
val type: Type
|
val type: Type
|
||||||
){
|
){
|
||||||
|
|
||||||
enum class Type(val serviceUUID: String?) {
|
enum class Type {
|
||||||
BEACON(null), THERMOMETER("a77db03a-9bc4-11ed-a8fc-0242ac120002")
|
BEACON, THERMOMETER
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ interface BleRepository {
|
||||||
|
|
||||||
fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>>
|
fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>>
|
||||||
|
|
||||||
suspend fun getBleBySerial(serial: String): Result<Ble, GetBleBySerial.GetBleException>
|
suspend fun getBleBySerial(serial: String) : Result<Flow<Ble>, BleException>
|
||||||
|
|
||||||
suspend fun getTemperatureHistoryBySerial(serial: String): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>>
|
suspend fun getTemperatureHistoryBySerial(serial: String): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
package llc.arma.ble.domain.usecase
|
package llc.arma.ble.domain.usecase
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import llc.arma.ble.domain.model.Ble
|
import llc.arma.ble.domain.model.Ble
|
||||||
import llc.arma.ble.domain.repository.BleRepository
|
import llc.arma.ble.domain.repository.BleRepository
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import llc.arma.ble.domain.Result
|
import llc.arma.ble.domain.Result
|
||||||
|
import llc.arma.ble.domain.common.BleException
|
||||||
|
|
||||||
class GetBleBySerial @Inject constructor(
|
class GetBleBySerial @Inject constructor(
|
||||||
private val bleRepository: BleRepository
|
private val bleRepository: BleRepository
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(serial: String): Result<Ble, GetBleException> =
|
suspend operator fun invoke(serial: String): Result<Flow<Ble>, BleException> =
|
||||||
bleRepository.getBleBySerial(serial)
|
bleRepository.getBleBySerial(serial)
|
||||||
|
|
||||||
sealed class GetBleException {
|
sealed class GetBleException {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue