Init
This commit is contained in:
parent
ffba61a55e
commit
ce95316494
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<runningDeviceTargetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="RUNNING_DEVICE_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="SERIAL_NUMBER" />
|
||||
<value value="55381e0a" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</runningDeviceTargetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2023-03-22T08:48:05.794601500Z" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -2,11 +2,18 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
|
||||
android:maxSdkVersion="30" />
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation"
|
||||
tools:targetApi="s" />
|
||||
|
||||
<uses-feature android:name="android.hardware.location.gps" />
|
||||
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package llc.arma.ble.app.ui
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
|
|
@ -7,28 +8,65 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import llc.arma.ble.app.ui.screen.main.MainScreen
|
||||
import llc.arma.ble.app.ui.theme.BleTheme
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
setContent {
|
||||
|
||||
BleTheme {
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize().navigationBarsPadding(),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
|
||||
val multiplePermissionsState = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
rememberMultiplePermissionsState(
|
||||
listOf(
|
||||
android.Manifest.permission.BLUETOOTH_SCAN,
|
||||
android.Manifest.permission.BLUETOOTH_CONNECT
|
||||
)
|
||||
)
|
||||
} else {
|
||||
rememberMultiplePermissionsState(
|
||||
listOf()
|
||||
)
|
||||
}
|
||||
|
||||
if(multiplePermissionsState.allPermissionsGranted) {
|
||||
|
||||
MainScreen()
|
||||
|
||||
} else {
|
||||
|
||||
LaunchedEffect(multiplePermissionsState){
|
||||
multiplePermissionsState.launchMultiplePermissionRequest()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
|
@ -49,6 +50,13 @@ fun BleListScreen(
|
|||
}
|
||||
)
|
||||
|
||||
if(state.bleList.isEmpty()){
|
||||
LinearProgressIndicator(
|
||||
strokeCap = StrokeCap.Round,
|
||||
modifier = Modifier.fillMaxWidth().height(3.dp)
|
||||
)
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
|
|
|
|||
|
|
@ -18,9 +18,17 @@ class BleListViewModel @Inject constructor(
|
|||
init {
|
||||
|
||||
getBleAroundFlow().onEach {
|
||||
it.fold(
|
||||
onSuccess = {
|
||||
setState {
|
||||
BleListContract.State(it)
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
}.launchIn(viewModelScope)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
|
@ -123,7 +124,11 @@ fun ConnectionScreen(
|
|||
private fun LoadingState(){
|
||||
Column {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||
CircularProgressIndicator(
|
||||
strokeCap = StrokeCap.Round,
|
||||
modifier = Modifier.align(Alignment.Center
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,31 @@ enum class SheetPage {
|
|||
INTERVAL, POWER, TEMPERATURE_HISTORY
|
||||
}
|
||||
|
||||
private val Boolean.localizedName: String
|
||||
get() {
|
||||
return if(this){
|
||||
"Включено"
|
||||
} else {
|
||||
"Выключено"
|
||||
}
|
||||
}
|
||||
|
||||
private val Ble.BleState.TX.localizedName: String
|
||||
get() {
|
||||
return when(this){
|
||||
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
|
||||
}.toString()
|
||||
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ThermometerScreen(
|
||||
|
|
@ -140,8 +165,6 @@ fun ThermometerScreen(
|
|||
|
||||
)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -197,7 +220,7 @@ fun ThermometerScreen(
|
|||
Text(
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = "${it} db"
|
||||
text = "${state.origin.state.tx.localizedName} db -> ${it.localizedName} db"
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -209,6 +232,39 @@ fun ThermometerScreen(
|
|||
|
||||
it.writeRequest.saveHistory?.let {
|
||||
|
||||
Box(
|
||||
modifier = Modifier.padding(
|
||||
vertical = 8.dp,
|
||||
horizontal = 8.dp
|
||||
)
|
||||
) {
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.padding(8.dp)
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Сохранять историю измерений"
|
||||
)
|
||||
Text(
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = "${state.origin.thermometerState.saveHistory.localizedName} -> ${it.localizedName}"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
it.writeRequest.historyInterval?.let {
|
||||
|
|
|
|||
|
|
@ -119,19 +119,6 @@ fun DisplayState(
|
|||
|
||||
}
|
||||
|
||||
if (ble.thermometerState.temperature.loading) {
|
||||
|
||||
CircularProgressIndicator()
|
||||
|
||||
} else {
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Refresh,
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -194,7 +181,7 @@ fun DisplayState(
|
|||
) {
|
||||
|
||||
Text(
|
||||
text = "Интервал измерний"
|
||||
text = "Интервал измерений"
|
||||
)
|
||||
Text(
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
package llc.arma.ble.app.ui.screen.thermometer.view
|
||||
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.gestures.scrollBy
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
|
|
@ -32,9 +36,15 @@ import kotlin.random.Random
|
|||
import kotlin.random.nextInt
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Refresh
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollState
|
||||
import com.patrykandpatrick.vico.core.axis.AxisPosition
|
||||
import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter
|
||||
import com.patrykandpatrick.vico.core.chart.scale.AutoScaleUp
|
||||
import com.patrykandpatrick.vico.core.entry.ChartEntry
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
import llc.arma.ble.domain.model.Ble
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
|
@ -80,7 +90,7 @@ fun TemperatureHistory(
|
|||
onClick = {
|
||||
viewModel.setEvent(TemperatureHistoryContract.Event.LoadHistory(ble.serial))
|
||||
},
|
||||
enabled = state is TemperatureHistoryContract.State.Display
|
||||
enabled = state.loadingHistoryState is ProgressState.Finished
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Refresh,
|
||||
|
|
@ -92,10 +102,12 @@ fun TemperatureHistory(
|
|||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
when (state) {
|
||||
is TemperatureHistoryContract.State.Display -> {
|
||||
when (state.loadingHistoryState) {
|
||||
is ProgressState.Finished -> {
|
||||
|
||||
val producer = state.history.mapIndexed { index, measurePoint ->
|
||||
Text(text = "${state.loadingHistoryState.data.size}")
|
||||
|
||||
val producer = state.loadingHistoryState.data.mapIndexed { index, measurePoint ->
|
||||
TemperatureEntry(measurePoint.date, index.toFloat(), measurePoint.value) }.let {
|
||||
ChartEntryModelProducer(it)
|
||||
}
|
||||
|
|
@ -107,14 +119,20 @@ fun TemperatureHistory(
|
|||
.orEmpty()
|
||||
}
|
||||
|
||||
val lineChart = lineChart()
|
||||
val lineChart = lineChart(
|
||||
spacing = 110.dp
|
||||
)
|
||||
|
||||
Box(modifier = Modifier.padding(8.dp)) {
|
||||
|
||||
val scrollState = rememberChartScrollState()
|
||||
|
||||
LaunchedEffect(scrollState.maxValue){
|
||||
scrollState.scrollBy(scrollState.maxValue)
|
||||
}
|
||||
|
||||
Chart(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1.5f),
|
||||
chartScrollState = scrollState,
|
||||
chart = lineChart,
|
||||
chartModelProducer = producer,
|
||||
startAxis = startAxis(),
|
||||
|
|
@ -122,12 +140,14 @@ fun TemperatureHistory(
|
|||
valueFormatter = axisValueFormatter,
|
||||
labelRotationDegrees = 0f,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1.5f),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
is TemperatureHistoryContract.State.Loading -> {
|
||||
is ProgressState.Indeterminate -> {
|
||||
|
||||
Box(modifier = Modifier.padding(8.dp)) {
|
||||
|
||||
|
|
@ -138,16 +158,38 @@ fun TemperatureHistory(
|
|||
){
|
||||
|
||||
CircularProgressIndicator(
|
||||
strokeCap = StrokeCap.Round,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
is ProgressState.Progress -> Box(modifier = Modifier.padding(8.dp)) {
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(2f),
|
||||
){
|
||||
|
||||
val progressAnimDuration = 1500
|
||||
val progressAnimation by animateFloatAsState(
|
||||
targetValue = state.loadingHistoryState.value,
|
||||
animationSpec = tween(durationMillis = progressAnimDuration, easing = FastOutSlowInEasing)
|
||||
)
|
||||
|
||||
CircularProgressIndicator(
|
||||
strokeCap = StrokeCap.Round,
|
||||
progress = progressAnimation,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -163,15 +205,9 @@ class TemperatureHistoryContract {
|
|||
|
||||
}
|
||||
|
||||
sealed class State : ViewState {
|
||||
|
||||
object Loading : State()
|
||||
|
||||
data class Display(
|
||||
var history : List<Ble.Thermometer.MeasurePoint>
|
||||
) : State()
|
||||
|
||||
}
|
||||
class State(
|
||||
val loadingHistoryState : ProgressState<List<Ble.Thermometer.MeasurePoint>>
|
||||
) : ViewState
|
||||
|
||||
sealed class Effect : ViewSideEffect {
|
||||
|
||||
|
|
@ -186,7 +222,9 @@ class TemperatureHistoryViewModel @Inject constructor(
|
|||
private val getTemperatureHistoryBySerial: GetTemperatureHistoryBySerial
|
||||
) : BaseViewModel<TemperatureHistoryContract.State, TemperatureHistoryContract.Event, TemperatureHistoryContract.Effect>() {
|
||||
|
||||
override fun setInitialState() = TemperatureHistoryContract.State.Loading
|
||||
override fun setInitialState() = TemperatureHistoryContract.State(
|
||||
ProgressState.Indeterminate
|
||||
)
|
||||
|
||||
override fun handleEvents(event: TemperatureHistoryContract.Event) {
|
||||
when(event){
|
||||
|
|
@ -201,14 +239,21 @@ class TemperatureHistoryViewModel @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
|
||||
setState {
|
||||
TemperatureHistoryContract.State.Loading
|
||||
TemperatureHistoryContract.State(ProgressState.Indeterminate)
|
||||
}
|
||||
|
||||
val history = getTemperatureHistoryBySerial(event.serial)
|
||||
|
||||
getTemperatureHistoryBySerial(event.serial).onEach {
|
||||
it.fold(
|
||||
onSuccess = {
|
||||
setState {
|
||||
TemperatureHistoryContract.State.Display(history)
|
||||
TemperatureHistoryContract.State(it)
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
|
||||
}
|
||||
)
|
||||
}.launchIn(this)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,24 +8,23 @@ import android.bluetooth.le.ScanResult
|
|||
import android.bluetooth.le.ScanSettings
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.ParcelUuid
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import llc.arma.ble.domain.Result
|
||||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
import llc.arma.ble.domain.model.Ble
|
||||
import llc.arma.ble.domain.model.BleInfo
|
||||
import llc.arma.ble.domain.repository.BleRepository
|
||||
import llc.arma.ble.domain.usecase.GetBleBySerial
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@Singleton
|
||||
class BleRepositoryImpl @Inject constructor(
|
||||
|
|
@ -69,12 +68,21 @@ class BleRepositoryImpl @Inject constructor(
|
|||
(this[idx].toUInt() and 0xFFu)
|
||||
|
||||
private val deviceCache = mutableMapOf<String, ScanResult>()
|
||||
|
||||
override fun getBleAroundFlow(): Flow<List<BleInfo>> {
|
||||
|
||||
val resultList = mutableMapOf<String, BleInfo>()
|
||||
|
||||
return callbackFlow {
|
||||
override fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>> {
|
||||
|
||||
return if (ActivityCompat.checkSelfPermission(
|
||||
app,
|
||||
Manifest.permission.BLUETOOTH_SCAN
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
|
||||
flow { emit(Result.failure(BleException.PermissionDenied)) }
|
||||
|
||||
} else {
|
||||
|
||||
callbackFlow {
|
||||
|
||||
val bleCallback = object : ScanCallback() {
|
||||
|
||||
|
|
@ -91,7 +99,7 @@ class BleRepositoryImpl @Inject constructor(
|
|||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
|
||||
if(result.scanRecord?.deviceName?.contains("ArmA") == true) {
|
||||
if (result.scanRecord?.deviceName?.contains("ArmA") == true) {
|
||||
|
||||
resultList[result.device.address] = result.info
|
||||
|
||||
|
|
@ -99,13 +107,21 @@ class BleRepositoryImpl @Inject constructor(
|
|||
|
||||
}
|
||||
|
||||
} else {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
send(
|
||||
Result.failure(BleException.PermissionDenied)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val bleScanner = app.getSystemService(BluetoothManager::class.java).adapter.bluetoothLeScanner
|
||||
val bleScanner =
|
||||
app.getSystemService(BluetoothManager::class.java).adapter.bluetoothLeScanner
|
||||
|
||||
bleScanner.startScan(
|
||||
listOf(),
|
||||
ScanSettings.Builder()
|
||||
|
|
@ -121,7 +137,7 @@ class BleRepositoryImpl @Inject constructor(
|
|||
schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
send(resultList.values.toList())
|
||||
send(Result.success(resultList.values.toList()))
|
||||
}
|
||||
}
|
||||
}, 100, 500)
|
||||
|
|
@ -136,6 +152,8 @@ class BleRepositoryImpl @Inject constructor(
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override suspend fun getBleBySerial(
|
||||
serial: String
|
||||
|
|
@ -178,10 +196,17 @@ class BleRepositoryImpl @Inject constructor(
|
|||
|
||||
BleInfo.Type.THERMOMETER -> {
|
||||
|
||||
val thermometer = readThermometerState(result).fold(
|
||||
onFailure = { _ ->
|
||||
return@launch it.resume(Result.failure(GetBleBySerial.GetBleException.BlePermissionDenied))
|
||||
},
|
||||
onSuccess = { return@fold it }
|
||||
)
|
||||
|
||||
Ble.Thermometer(
|
||||
info = info,
|
||||
state = state,
|
||||
thermometerState = readThermometerState(result)
|
||||
thermometerState = thermometer
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -204,33 +229,54 @@ class BleRepositoryImpl @Inject constructor(
|
|||
|
||||
private suspend fun readThermometerState(
|
||||
record: ScanResult
|
||||
): Ble.Thermometer.ThermometerState {
|
||||
): Result<Ble.Thermometer.ThermometerState, BleException> {
|
||||
|
||||
return Ble.Thermometer.ThermometerState(
|
||||
temperature = readTemperature(record),
|
||||
val temperature = readTemperature(record).fold(
|
||||
onFailure = {
|
||||
return Result.failure(it)
|
||||
},
|
||||
onSuccess = { return@fold it }
|
||||
)
|
||||
|
||||
val history = readHistoryInterval(record).fold(
|
||||
onFailure = {
|
||||
return Result.failure(it)
|
||||
},
|
||||
onSuccess = { return@fold it }
|
||||
)
|
||||
|
||||
return Result.success(
|
||||
Ble.Thermometer.ThermometerState(
|
||||
temperature = temperature,
|
||||
saveHistory = record.timerEnabled,
|
||||
historyInterval = readHistoryInterval(record)
|
||||
historyInterval = history
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private suspend fun readTemperature(
|
||||
record: ScanResult
|
||||
): Float {
|
||||
): Result<Float, BleException> {
|
||||
|
||||
val dataResult = readCharacteristic(
|
||||
device = record.device,
|
||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||
characteristicId = UUID.fromString("00002a6e-0000-1000-8000-00805f9b34fb")
|
||||
).fold(
|
||||
onFailure = {
|
||||
return Result.failure(it)
|
||||
},
|
||||
onSuccess = { return@fold it }
|
||||
)
|
||||
|
||||
return (dataResult[0] + dataResult[1] * 256).toFloat() / 100f
|
||||
return Result.success((dataResult[0] + dataResult[1] * 256).toFloat() / 100f)
|
||||
|
||||
}
|
||||
|
||||
private suspend fun readHistoryInterval(
|
||||
record: ScanResult
|
||||
): Long {
|
||||
): Result<Long, BleException> {
|
||||
|
||||
writeCharacteristic(
|
||||
device = record.device,
|
||||
|
|
@ -243,19 +289,26 @@ class BleRepositoryImpl @Inject constructor(
|
|||
device = record.device,
|
||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb")
|
||||
).fold(
|
||||
onFailure = {
|
||||
return Result.failure(it)
|
||||
},
|
||||
onSuccess = { return@fold it }
|
||||
)
|
||||
|
||||
return if(dataResult.size == 4){
|
||||
return Result.success(
|
||||
if(dataResult.size == 4){
|
||||
dataResult.getUIntAt(0).toLong()
|
||||
}else{
|
||||
0
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
override suspend fun getTemperatureHistoryBySerial(
|
||||
serial: String
|
||||
): List<Ble.Thermometer.MeasurePoint> {
|
||||
): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>> = flow {
|
||||
|
||||
fun ByteArray.getUIntAt(idx: Int) =
|
||||
((this[idx + 3].toUInt() and 0xFFu) shl 24) or
|
||||
|
|
@ -265,6 +318,8 @@ class BleRepositoryImpl @Inject constructor(
|
|||
|
||||
deviceCache[serial]?.device?.let { device ->
|
||||
|
||||
emit(Result.success(ProgressState.Indeterminate))
|
||||
|
||||
writeCharacteristic(
|
||||
device = device,
|
||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||
|
|
@ -276,6 +331,12 @@ class BleRepositoryImpl @Inject constructor(
|
|||
device = device,
|
||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb"),
|
||||
).fold(
|
||||
onFailure = {
|
||||
emit(Result.failure(it))
|
||||
return@flow
|
||||
},
|
||||
onSuccess = { return@fold it }
|
||||
)
|
||||
|
||||
writeCharacteristic(
|
||||
|
|
@ -295,6 +356,12 @@ class BleRepositoryImpl @Inject constructor(
|
|||
device = device,
|
||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb"),
|
||||
).fold(
|
||||
onFailure = {
|
||||
emit(Result.failure(it))
|
||||
return@flow
|
||||
},
|
||||
onSuccess = { return@fold it }
|
||||
)
|
||||
|
||||
if(firstPackageResponse[0] == 250.toByte()){
|
||||
|
|
@ -311,11 +378,14 @@ class BleRepositoryImpl @Inject constructor(
|
|||
(it[0] + it[1] * 256).toFloat() / 100f
|
||||
}.toMutableList()
|
||||
|
||||
Log.d("read", temperaturePackage.size.toString())
|
||||
var dataCount = firstPackageResponse[1].toUByte()
|
||||
val totalDataSize = dataCount.toInt() + temperaturePackage.size
|
||||
|
||||
var dataCount = firstPackageResponse[1]
|
||||
emit(Result.success(ProgressState.Progress(0f / totalDataSize.toFloat())))
|
||||
delay(100)
|
||||
emit(Result.success(ProgressState.Progress(dataCount.toFloat() / totalDataSize.toFloat())))
|
||||
|
||||
while(dataCount != 0.toByte()){
|
||||
while(dataCount != 0.toUByte()){
|
||||
|
||||
writeCharacteristic(
|
||||
device = device,
|
||||
|
|
@ -328,9 +398,15 @@ class BleRepositoryImpl @Inject constructor(
|
|||
device = device,
|
||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||
characteristicId = UUID.fromString("0000b2d8-0000-1000-8000-00805f9b34fb"),
|
||||
).fold(
|
||||
onFailure = {
|
||||
emit(Result.failure(it))
|
||||
return@flow
|
||||
},
|
||||
onSuccess = { return@fold it }
|
||||
)
|
||||
|
||||
dataCount = readResponse.get(1)
|
||||
dataCount = readResponse[1].toUByte()
|
||||
|
||||
temperatureDataArray = readResponse.toList().subList(2, readResponse.size)
|
||||
|
||||
|
|
@ -340,25 +416,27 @@ class BleRepositoryImpl @Inject constructor(
|
|||
}
|
||||
)
|
||||
|
||||
Log.d("read",(temperatureDataArray.size / 2).toString())
|
||||
emit(Result.success(ProgressState.Progress(totalDataSize.toFloat() / temperaturePackage.size.toFloat())))
|
||||
|
||||
}
|
||||
|
||||
Log.d("metadata", interval.toString() + " " + lastMeasureSystemTime.toString())
|
||||
|
||||
return temperaturePackage.withIndex().map {
|
||||
emit(
|
||||
Result.success(
|
||||
ProgressState.Finished(
|
||||
temperaturePackage.withIndex().map {
|
||||
Ble.Thermometer.MeasurePoint(
|
||||
date = lastMeasureSystemTime - (((temperaturePackage.size - 1) - it.index) * interval),
|
||||
value = it.value
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
|
||||
}
|
||||
|
||||
override suspend fun writeBle(ble: Ble) {
|
||||
|
|
@ -381,6 +459,9 @@ class BleRepositoryImpl @Inject constructor(
|
|||
|
||||
request.saveHistory?.let { writeSaveEnabled(result.device, it) }
|
||||
|
||||
deviceCache.remove(serial)
|
||||
resultList.remove(serial)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -457,24 +538,26 @@ class BleRepositoryImpl @Inject constructor(
|
|||
private suspend fun writeSaveEnabled(
|
||||
device: BluetoothDevice,
|
||||
enabled: Boolean
|
||||
) {
|
||||
): Result<Unit, BleException> {
|
||||
|
||||
writeCharacteristic(
|
||||
device = device,
|
||||
serviceId = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002"),
|
||||
characteristicId = UUID.fromString("0000b6f2-0000-1000-8000-00805f9b34fb"),
|
||||
writeData = mutableListOf<Byte>(3).apply {
|
||||
writeData = mutableListOf<Byte>(4).apply {
|
||||
add(if(enabled) 1 else 0)
|
||||
}.toByteArray()
|
||||
)
|
||||
|
||||
return Result.success(Unit)
|
||||
|
||||
}
|
||||
|
||||
private suspend fun readCharacteristic(
|
||||
device: BluetoothDevice,
|
||||
serviceId: UUID,
|
||||
characteristicId: UUID
|
||||
): ByteArray = suspendCoroutine {
|
||||
): Result<ByteArray, BleException> = suspendCancellableCoroutine {
|
||||
|
||||
val callback = object : BluetoothGattCallback() {
|
||||
|
||||
|
|
@ -492,10 +575,10 @@ class BleRepositoryImpl @Inject constructor(
|
|||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
gatt?.discoverServices()
|
||||
} else {
|
||||
it.resume(Result.failure(BleException.PermissionDenied))
|
||||
}
|
||||
|
||||
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -512,16 +595,16 @@ class BleRepositoryImpl @Inject constructor(
|
|||
service.uuid == serviceId
|
||||
}?.characteristics?.firstOrNull { characteristic ->
|
||||
characteristic.uuid == characteristicId
|
||||
}?.let {
|
||||
}?.let { char ->
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || ActivityCompat.checkSelfPermission(
|
||||
app,
|
||||
Manifest.permission.BLUETOOTH_CONNECT
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
|
||||
gatt.readCharacteristic(it)
|
||||
|
||||
gatt.readCharacteristic(char)
|
||||
} else {
|
||||
it.resume(Result.failure(BleException.PermissionDenied))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -537,14 +620,29 @@ class BleRepositoryImpl @Inject constructor(
|
|||
status: Int
|
||||
) {
|
||||
super.onCharacteristicRead(gatt, characteristic, value, status)
|
||||
|
||||
it.resume(value)
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
app,
|
||||
Manifest.permission.BLUETOOTH_CONNECT
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
it.resume(Result.failure(BleException.PermissionDenied))
|
||||
}else {
|
||||
gatt.disconnect()
|
||||
it.resume(Result.success(value))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
app,
|
||||
Manifest.permission.BLUETOOTH_CONNECT
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
it.resume(Result.failure(BleException.PermissionDenied))
|
||||
} else {
|
||||
device.connectGatt(app, true, callback)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -553,7 +651,7 @@ class BleRepositoryImpl @Inject constructor(
|
|||
serviceId: UUID,
|
||||
characteristicId: UUID,
|
||||
writeData: ByteArray
|
||||
) = suspendCoroutine {
|
||||
) = suspendCancellableCoroutine {
|
||||
|
||||
val callback = object : BluetoothGattCallback() {
|
||||
|
||||
|
|
@ -573,8 +671,6 @@ class BleRepositoryImpl @Inject constructor(
|
|||
gatt?.discoverServices()
|
||||
}
|
||||
|
||||
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -591,7 +687,7 @@ class BleRepositoryImpl @Inject constructor(
|
|||
service.uuid == serviceId
|
||||
}?.characteristics?.firstOrNull { characteristic ->
|
||||
characteristic.uuid == characteristicId
|
||||
}?.let {
|
||||
}?.let { char ->
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || ActivityCompat.checkSelfPermission(
|
||||
app,
|
||||
|
|
@ -600,10 +696,10 @@ class BleRepositoryImpl @Inject constructor(
|
|||
) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
gatt.writeCharacteristic(it, writeData, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
|
||||
gatt.writeCharacteristic(char, writeData, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
|
||||
}else{
|
||||
it.value = writeData
|
||||
gatt.writeCharacteristic(it)
|
||||
char.value = writeData
|
||||
gatt.writeCharacteristic(char)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -620,11 +716,21 @@ class BleRepositoryImpl @Inject constructor(
|
|||
status: Int
|
||||
) {
|
||||
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
app,
|
||||
Manifest.permission.BLUETOOTH_CONNECT
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
return
|
||||
} else {
|
||||
gatt.disconnect()
|
||||
it.resume(Unit)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
device.connectGatt(app, true, callback)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package llc.arma.ble.domain.common
|
||||
|
||||
sealed class BleException {
|
||||
|
||||
object PermissionDenied : BleException()
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package llc.arma.ble.domain.common
|
||||
|
||||
sealed class ProgressState<out T> {
|
||||
|
||||
object Indeterminate : ProgressState<Nothing>()
|
||||
|
||||
data class Progress(
|
||||
val value: Float
|
||||
) : ProgressState<Nothing>()
|
||||
|
||||
data class Finished<T>(
|
||||
val data: T
|
||||
) : ProgressState<T>()
|
||||
|
||||
}
|
||||
|
|
@ -2,17 +2,19 @@ package llc.arma.ble.domain.repository
|
|||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import llc.arma.ble.domain.Result
|
||||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
import llc.arma.ble.domain.model.Ble
|
||||
import llc.arma.ble.domain.model.BleInfo
|
||||
import llc.arma.ble.domain.usecase.GetBleBySerial
|
||||
|
||||
interface BleRepository {
|
||||
|
||||
fun getBleAroundFlow(): Flow<List<BleInfo>>
|
||||
fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>>
|
||||
|
||||
suspend fun getBleBySerial(serial: String): Result<Ble, GetBleBySerial.GetBleException>
|
||||
|
||||
suspend fun getTemperatureHistoryBySerial(serial: String): List<Ble.Thermometer.MeasurePoint>
|
||||
suspend fun getTemperatureHistoryBySerial(serial: String): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>>
|
||||
|
||||
suspend fun writeBle(ble: Ble)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package llc.arma.ble.domain.usecase
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import llc.arma.ble.domain.Result
|
||||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.model.Ble
|
||||
import llc.arma.ble.domain.model.BleInfo
|
||||
import llc.arma.ble.domain.repository.BleRepository
|
||||
|
|
@ -10,6 +12,6 @@ class GetBleAroundFlow @Inject constructor(
|
|||
private val bleRepository: BleRepository
|
||||
) {
|
||||
|
||||
operator fun invoke(): Flow<List<BleInfo>> = bleRepository.getBleAroundFlow()
|
||||
operator fun invoke(): Flow<Result<List<BleInfo>, BleException>> = bleRepository.getBleAroundFlow()
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
package llc.arma.ble.domain.usecase
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import llc.arma.ble.domain.Result
|
||||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
import llc.arma.ble.domain.model.Ble
|
||||
import llc.arma.ble.domain.repository.BleRepository
|
||||
import javax.inject.Inject
|
||||
|
|
@ -8,7 +12,7 @@ class GetTemperatureHistoryBySerial @Inject constructor(
|
|||
private val bleRepository: BleRepository
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(serial: String): List<Ble.Thermometer.MeasurePoint> {
|
||||
suspend operator fun invoke(serial: String): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>> {
|
||||
|
||||
return bleRepository.getTemperatureHistoryBySerial(serial)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ class WriteBle @Inject constructor(
|
|||
bleRepository.writeBle(ble)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(serial: String, request: Ble.Thermometer.WriteRequest){
|
||||
suspend operator fun invoke(
|
||||
serial: String,
|
||||
request: Ble.Thermometer.WriteRequest
|
||||
){
|
||||
bleRepository.writeBle(serial, request)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue