From ab27faa8329724a78661012024a9aea8a3d30ab8 Mon Sep 17 00:00:00 2001 From: Vineyro Date: Fri, 18 Oct 2024 17:00:51 +0700 Subject: [PATCH] filter saving --- .idea/misc.xml | 3 +- app/build.gradle | 2 +- .../llc/arma/ble/app/ui/mapper/BleMapper.kt | 3 +- .../arma/ble/app/ui/mapper/BleViewMapper.kt | 3 +- .../java/llc/arma/ble/app/ui/model/BleView.kt | 5 +- .../ui/screen/inspection/host/HostContract.kt | 14 +- .../ui/screen/inspection/host/HostScreen.kt | 24 +- .../screen/inspection/host/HostViewModel.kt | 33 +- .../inspection/host/view/DisplayState.kt | 26 +- .../inspection/host/view/IntervalEdit.kt | 194 +++++---- .../inspection/host/view/ReadIntervalEdit.kt | 102 +++++ .../ui/screen/inspection/host/view/Write.kt | 50 ++- .../ble/data/repository/BleRepositoryImpl.kt | 384 ++++++++---------- .../ble/data/repository/ReadHostHistory.kt | 114 +++--- .../data/repository/SettingsRepositoryImpl.kt | 1 - .../java/llc/arma/ble/domain/model/Ble.kt | 25 +- 16 files changed, 608 insertions(+), 375 deletions(-) create mode 100644 app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/ReadIntervalEdit.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index 8978d23..9f71c83 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,7 @@ + - + diff --git a/app/build.gradle b/app/build.gradle index 9bf6b73..64f0e7f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,7 +20,7 @@ android { minSdk 26 targetSdk 34 versionCode 41 - versionName "1.4.12" + versionName "1.4.13" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt index 9c95a12..68772d5 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt @@ -52,7 +52,8 @@ class BleMapper @Inject constructor( tx = txMapper.map(input.state.tx) ), hostState = BleView.Host.HostState( - historyInterval = input.hostState.historyInterval + historyInterval = input.hostState.historyInterval, + readInterval = input.hostState.readInterval ) ) } diff --git a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt index f86acbf..388b748 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt @@ -52,7 +52,8 @@ class BleViewMapper @Inject constructor( tx = txMapper.map(input.state.tx) ), hostState = Ble.Host.HostState( - historyInterval = input.hostState.historyInterval + historyInterval = input.hostState.historyInterval, + readInterval = input.hostState.readInterval ) ) } diff --git a/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt b/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt index 122f9ea..12d5b24 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt @@ -1,6 +1,7 @@ package llc.arma.ble.app.ui.model import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import llc.arma.ble.domain.model.Ble @@ -65,8 +66,10 @@ sealed class BleView( class HostState( historyInterval: Long, + readInterval: Long ) { - var historyInterval by mutableStateOf(historyInterval) + var historyInterval by mutableLongStateOf(historyInterval) + var readInterval by mutableLongStateOf(readInterval) } } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostContract.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostContract.kt index ba3b504..dc99813 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostContract.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostContract.kt @@ -31,7 +31,15 @@ class HostContract { data object OnShowIntervalEdit : Event() - data class OnSaveIntervalChanged(val interval: Long) : Event() + data class OnSaveIntervalChanged( + val interval: Long + ) : Event() + + data object OnShowReadIntervalEdit : Event() + + data class OnSaveReadIntervalChanged( + val interval: Long + ) : Event() data object OnNavigateUpClicked : Event() @@ -87,6 +95,10 @@ class HostContract { data object ShowIntervalPicker : Effect() + data object HideReadIntervalPicker : Effect() + + data object ShowReadIntervalPicker : Effect() + sealed class Navigation : Effect() { data object NavigateToChangePassword : Navigation() diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostScreen.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostScreen.kt index 2af021d..e1c47bf 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostScreen.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostScreen.kt @@ -21,11 +21,12 @@ import llc.arma.ble.app.ui.common.rememberBottomDialogState import llc.arma.ble.app.ui.screen.inspection.host.view.DisplayState import llc.arma.ble.app.ui.screen.inspection.host.view.IntervalEdit import llc.arma.ble.app.ui.screen.inspection.host.view.PowerEdit +import llc.arma.ble.app.ui.screen.inspection.host.view.ReadIntervalEdit import llc.arma.ble.app.ui.screen.inspection.host.view.Write import llc.arma.ble.domain.model.Ble enum class SheetPage { - WRITE, POWER_EDIT, INTERVAL_EDIT + WRITE, POWER_EDIT, INTERVAL_EDIT, READ_INTERVAL_EDIT } @Composable @@ -72,6 +73,15 @@ fun HostScreen( delay(100) sheetPage = SheetPage.INTERVAL_EDIT } + + HostContract.Effect.HideReadIntervalPicker -> launch { + sheetPage = null + } + HostContract.Effect.ShowReadIntervalPicker -> launch { + sheetPage = null + delay(100) + sheetPage = SheetPage.READ_INTERVAL_EDIT + } } }.launchIn(this) } @@ -122,6 +132,18 @@ fun HostScreen( ) } } + SheetPage.READ_INTERVAL_EDIT -> bottomDialog.show { + val currentState = viewModel.viewState.value + + if(currentState is HostContract.State.Display) { + ReadIntervalEdit( + state = currentState.host, + onEvent = { + viewModel.setEvent(it) + } + ) + } + } else -> { bottomDialog.hide() } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostViewModel.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostViewModel.kt index 3b20e87..f5e3111 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostViewModel.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/HostViewModel.kt @@ -35,9 +35,39 @@ class HostViewModel @Inject constructor( is HostContract.Event.OnShowHostBleTable -> reduce(viewState.value, event) is HostContract.Event.OnSaveIntervalChanged -> reduce(viewState.value, event) is HostContract.Event.OnShowIntervalEdit -> reduce(viewState.value, event) + is HostContract.Event.OnSaveReadIntervalChanged -> reduce(viewState.value, event) + is HostContract.Event.OnShowReadIntervalEdit -> reduce(viewState.value, event) } } + private fun reduce( + state: HostContract.State, + event: HostContract.Event.OnSaveReadIntervalChanged + ) { + + if(state is HostContract.State.Display) { + + state.host.hostState.readInterval = event.interval + + } + + setEffect { + HostContract.Effect.HideReadIntervalPicker + } + + } + + private fun reduce( + state: HostContract.State, + event: HostContract.Event.OnShowReadIntervalEdit + ) { + + setEffect { + HostContract.Effect.ShowReadIntervalPicker + } + + } + private fun reduce( state: HostContract.State, event: HostContract.Event.OnSaveIntervalChanged @@ -195,7 +225,8 @@ class HostViewModel @Inject constructor( val writeRequest = Ble.Host.WriteRequest( tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx, - interval = if(newBle.hostState.historyInterval == state.origin.hostState.historyInterval) null else newBle.hostState.historyInterval + interval = if(newBle.hostState.historyInterval == state.origin.hostState.historyInterval) null else newBle.hostState.historyInterval, + readInterval = if(newBle.hostState.readInterval == state.origin.hostState.readInterval) null else newBle.hostState.readInterval, ) setState { diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/DisplayState.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/DisplayState.kt index 79d4dee..e389b7a 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/DisplayState.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/DisplayState.kt @@ -67,12 +67,12 @@ fun DisplayState( onEvent(HostContract.Event.OnPowerEdit) } - val hours = - ble.hostState.historyInterval / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour - val minutes = - (ble.hostState.historyInterval - (hours * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour)) / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInMinute - val seconds = - (ble.hostState.historyInterval - (hours * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour) - (minutes * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInMinute)) / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInSecond + var hours = + ble.hostState.historyInterval / millisInHour + var minutes = + (ble.hostState.historyInterval - (hours * millisInHour)) / millisInMinute + var seconds = + (ble.hostState.historyInterval - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond BleMenuItem( title = "Интервал измерений", @@ -82,6 +82,20 @@ fun DisplayState( onEvent(HostContract.Event.OnShowIntervalEdit) } + hours = ble.hostState.readInterval / millisInHour + minutes = + (ble.hostState.readInterval - (hours * millisInHour)) / millisInMinute + seconds = + (ble.hostState.readInterval - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond + + BleMenuItem( + title = "Интервал чтения", + subtitle = "$hours ч. $minutes мин. $seconds сек.", + icon = rememberVectorPainter(Icons.Rounded.KeyboardArrowDown) + ) { + onEvent(HostContract.Event.OnShowReadIntervalEdit) + } + BleMenuItem( title = "График измерений", icon = rememberVectorPainter(Icons.Rounded.KeyboardArrowRight) diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/IntervalEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/IntervalEdit.kt index 2b98c76..be27a2e 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/IntervalEdit.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/IntervalEdit.kt @@ -47,27 +47,6 @@ fun IntervalEdit( mutableIntStateOf((state.hostState.historyInterval).toInt()) } - val maxInterval = 10 * 24 * 60 * 60 * 1000 - val minInterval = 10_000 - - if(value > maxInterval){ - value = maxInterval - } - - if(value < minInterval){ - value = minInterval - } - - val maxSeconds = maxInterval / millisInSecond - val maxMinutes = maxInterval / millisInMinute - val maxHours = maxInterval / millisInHour - val maxDays = maxInterval / millisInDay - - val dayValue = value / millisInDay - val hourValue = (value - (dayValue * millisInDay)) / millisInHour - val minutesValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour)) / millisInMinute - val secondsValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour) - (minutesValue * millisInMinute)) / millisInSecond - Column( modifier = Modifier ) { @@ -80,66 +59,11 @@ fun IntervalEdit( Spacer(modifier = Modifier.height(16.dp)) - - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.align(Alignment.CenterHorizontally) + DurationPicker( + modifier = Modifier.align(Alignment.CenterHorizontally), + value = value ) { - - NumberPicker( - range = -1..maxDays, - value = dayValue, - onValueChanged = { - value = (it * millisInDay) + (hourValue * millisInHour) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond) - } - ) - - Spacer(modifier = Modifier.width(8.dp)) - - Text(text = "Д.") - - Spacer(modifier = Modifier.width(16.dp)) - - NumberPicker( - range = -1..maxHours, - value = hourValue, - onValueChanged = { - value = (it * millisInHour) + (dayValue * millisInDay) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond) - } - ) - - Spacer(modifier = Modifier.width(8.dp)) - - Text(text = "Ч.") - - Spacer(modifier = Modifier.width(16.dp)) - - NumberPicker( - range = -1..maxMinutes, - value = minutesValue, - onValueChanged = { - value = (secondsValue * millisInSecond) + (it * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour) - } - ) - - Spacer(modifier = Modifier.width(8.dp)) - - Text(text = "М.") - - Spacer(modifier = Modifier.width(16.dp)) - - NumberPicker( - range = -1..maxSeconds, - value = secondsValue, - onValueChanged = { - value = (it * millisInSecond) + (minutesValue * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour) - } - ) - - Spacer(modifier = Modifier.width(8.dp)) - - Text(text = "С.") - + value = it } Spacer(modifier = Modifier.height(16.dp)) @@ -177,6 +101,116 @@ fun IntervalEdit( } +@Composable +fun DurationPicker( + modifier: Modifier, + maxInterval: Int = 10 * 24 * 60 * 60 * 1000, + minInterval: Int = 10_000, + seconds: Boolean = true, + minutes: Boolean = true, + hours: Boolean = true, + days: Boolean = true, + value: Int, + onChanged: (duration: Int) -> Unit +){ + + if(value > maxInterval){ + onChanged(maxInterval) + } + + if(value < minInterval){ + onChanged(minInterval) + } + + val maxSeconds = maxInterval / millisInSecond + val maxMinutes = maxInterval / millisInMinute + val maxHours = maxInterval / millisInHour + val maxDays = maxInterval / millisInDay + + val dayValue = value / millisInDay + val hourValue = (value - (dayValue * millisInDay)) / millisInHour + val minutesValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour)) / millisInMinute + val secondsValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour) - (minutesValue * millisInMinute)) / millisInSecond + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + ) { + + if(days) { + + NumberPicker( + range = -1..maxDays, + value = dayValue, + onValueChanged = { + onChanged((it * millisInDay) + (hourValue * millisInHour) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond)) + } + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text(text = "Д.") + + } + + if(hours) { + + Spacer(modifier = Modifier.width(16.dp)) + + NumberPicker( + range = -1..maxHours, + value = hourValue, + onValueChanged = { + onChanged((it * millisInHour) + (dayValue * millisInDay) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond)) + } + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text(text = "Ч.") + + } + + if(minutes) { + + Spacer(modifier = Modifier.width(16.dp)) + + NumberPicker( + range = -1..maxMinutes, + value = minutesValue, + onValueChanged = { + onChanged((secondsValue * millisInSecond) + (it * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour)) + } + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text(text = "М.") + + } + + if(seconds) { + + Spacer(modifier = Modifier.width(16.dp)) + + NumberPicker( + range = -1..maxSeconds, + value = secondsValue, + onValueChanged = { + onChanged((it * millisInSecond) + (minutesValue * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour)) + } + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text(text = "С.") + + } + + } + +} + const val millisInSecond = 1000 const val millisInMinute = millisInSecond * 60 const val millisInHour = millisInMinute * 60 diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/ReadIntervalEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/ReadIntervalEdit.kt new file mode 100644 index 0000000..fca4412 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/ReadIntervalEdit.kt @@ -0,0 +1,102 @@ +package llc.arma.ble.app.ui.screen.inspection.host.view + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.SizeTransform +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.KeyboardArrowDown +import androidx.compose.material.icons.rounded.KeyboardArrowUp +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import llc.arma.ble.app.ui.model.BleView +import llc.arma.ble.app.ui.screen.inspection.host.HostContract + +@Composable +fun ReadIntervalEdit( + state: BleView.Host, + onEvent: (HostContract.Event) -> Unit, +){ + + var value by remember(state.hostState.readInterval) { + mutableIntStateOf((state.hostState.readInterval).toInt()) + } + + Column( + modifier = Modifier + ) { + + Text( + modifier = Modifier.padding(horizontal = 12.dp), + text = "Интервал чтения", + style = MaterialTheme.typography.titleLarge + ) + + Spacer(modifier = Modifier.height(16.dp)) + + DurationPicker( + modifier = Modifier.align(Alignment.CenterHorizontally), + value = value + ) { + value = it + } + + Spacer(modifier = Modifier.height(16.dp)) + + Surface( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + .height(50.dp), + shape = CircleShape, + color = MaterialTheme.colorScheme.primaryContainer, + onClick = { + onEvent( + HostContract.Event.OnSaveReadIntervalChanged( + value.toLong() + ) + ) + } + ) { + + Box(modifier = Modifier.fillMaxSize()) { + + Text( + modifier = Modifier.align(Alignment.Center), + color = MaterialTheme.colorScheme.background, + style = MaterialTheme.typography.labelLarge, + text = "Применить" + ) + + } + + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/Write.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/Write.kt index 88ed1ee..c99cfa1 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/Write.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/host/view/Write.kt @@ -49,7 +49,7 @@ fun Write( when (state) { is HostContract.State.Display.WriteState.DisplayPreview -> { - if(state.writeRequest.tx != null || state.writeRequest.interval != null ) { + if(state.writeRequest.tx != null || state.writeRequest.interval != null || state.writeRequest.readInterval !== null) { state.writeRequest.tx?.let { Box( @@ -110,9 +110,51 @@ fun Write( text = "Интервал измерений" ) - val hours = it / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour - val minutes = (it - (hours * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour)) / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInMinute - val seconds = (it - (hours * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour) - (minutes * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInMinute)) / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInSecond + val hours = it / millisInHour + val minutes = (it - (hours * millisInHour)) / millisInMinute + val seconds = (it - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond + + Text( + color = MaterialTheme.colorScheme.secondary, + style = MaterialTheme.typography.bodyMedium, + text = "$hours ч. $minutes мин. $seconds сек." + ) + + } + + } + + } + + } + + state.writeRequest.readInterval?.let { + + Box( + modifier = Modifier.padding( + vertical = 0.dp, + horizontal = 8.dp + ) + ) { + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .padding(8.dp) + ) { + + Column( + modifier = Modifier.weight(1f) + ) { + + Text( + text = "Интервал чтения" + ) + + val hours = it / millisInHour + val minutes = (it - (hours * millisInHour)) / millisInMinute + val seconds = (it - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond Text( color = MaterialTheme.colorScheme.secondary, diff --git a/app/src/main/java/llc/arma/ble/data/repository/BleRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/repository/BleRepositoryImpl.kt index d3b9449..1f9b7e8 100644 --- a/app/src/main/java/llc/arma/ble/data/repository/BleRepositoryImpl.kt +++ b/app/src/main/java/llc/arma/ble/data/repository/BleRepositoryImpl.kt @@ -35,6 +35,7 @@ import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftViewMode import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic import no.nordicsemi.android.kotlin.ble.core.scanner.BleNumOfMatches import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanMode import no.nordicsemi.android.kotlin.ble.core.scanner.BleScannerCallbackType @@ -65,9 +66,6 @@ val passwordWriteUUID: UUID = UUID.fromString("a77db6f2-9bc4-11ed-a8fc-0242ac120 val txWriteUUID: UUID = UUID.fromString("00002a07-0000-1000-8000-00805f9b34fb") val flashWriteUUID: UUID = UUID.fromString("a77db6f2-9bc4-11ed-a8fc-0242ac120002") - - - @Singleton class BleRepositoryImpl @Inject constructor( private val app: Application, @@ -135,200 +133,136 @@ class BleRepositoryImpl @Inject constructor( serial: String ): Result, BleException> { - resultList[serial]?.let { result -> + val initialBle = resultList[serial] - return when(result.type) { - BleInfo.Type.ACCELEROMETER -> { + return if(initialBle == null){ - val tState = readAccelState( - result.serial, - result.tableStatus + Result.failure(BleException.UnexpectedResponse) + + } else { + + fun BleInfo.updateBleInfo(): BleInfo { + return copy( + rssi = if((SystemClock.elapsedRealtime() - scanTime) > 15_000) { + null + } else { rssi } + ) + } + + fun BleInfo.updateState(): Ble.BleState { + return Ble.BleState( + tx = Ble.BleState.TX.fromByte(tx.toByte()) + ?: Ble.BleState.TX.ZERO + ) + } + + val firstResult = when(initialBle.type){ + BleInfo.Type.HOST -> { + + val tState = readHostState( + initialBle.serial ).fold( - onFailure = { - return Result.failure(it) - }, - onSuccess = { - it - } + onFailure = { return Result.failure(it) }, + onSuccess = { it } ) - Result.success( - flow { - - while (true) { - - resultList[serial]?.let { newResult -> - - emit( - - Ble.Accelerometer( - info = newResult.copy( - rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) { - null - } else { - newResult.rssi - } - ), - state = Ble.BleState( - tx = Ble.BleState.TX.fromByte(result.tx.toByte()) - ?: Ble.BleState.TX.ZERO - ), - accelerometerState = tState - ) - - ) - - } - - delay(1_000) - - } - - } + Ble.Host( + info = initialBle.updateBleInfo(), + state = initialBle.updateState(), + hostState = tState ) + } BleInfo.Type.BEACON -> { - Result.success( - flow { - - while (true) { - - resultList[serial]?.let { newResult -> - - val state = Ble.BleState( - tx = Ble.BleState.TX.fromByte(result.tx.toByte()) - ?: Ble.BleState.TX.ZERO - ) - - emit( - - Ble.Beacon( - info = newResult.copy( - rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) { - null - } else { - newResult.rssi - } - - ), - state = state - ) - - ) - - } - - delay(1_000) - - } - - } + Ble.Beacon( + info = initialBle.updateBleInfo(), + state = initialBle.updateState(), ) } BleInfo.Type.THERMOMETER -> { - val tState = readThermometerState(result.serial, result.tableStatus).fold( - onFailure = { - return Result.failure(it) - }, - onSuccess = { - it - } + val tState = readThermometerState( + initialBle.serial, + initialBle.tableStatus + ).fold( + onFailure = { return Result.failure(it) }, + onSuccess = { it } ) - Result.success( - flow { - - while (true) { - - resultList[serial]?.let { newResult -> - - val state = Ble.BleState( - tx = Ble.BleState.TX.fromByte(result.tx.toByte()) - ?: Ble.BleState.TX.ZERO - ) - - emit( - - Ble.Thermometer( - info = newResult.copy( - rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) { - null - } else { - newResult.rssi - } - - ), - state = state, - thermometerState = tState - ) - - ) - - } - - delay(1_000) - - } - - } + Ble.Thermometer( + info = initialBle.updateBleInfo(), + state = initialBle.updateState(), + thermometerState = tState ) } + BleInfo.Type.ACCELEROMETER -> { - BleInfo.Type.HOST -> { - - val tState = readHostState(result.serial).fold( - onFailure = { - return Result.failure(it) - }, - onSuccess = { - it - } + val tState = readAccelState( + initialBle.serial, + initialBle.tableStatus + ).fold( + onFailure = { return Result.failure(it) }, + onSuccess = { it } ) - Result.success( - flow { - - while (true) { - - resultList[serial]?.let { newResult -> - - val state = Ble.BleState( - tx = Ble.BleState.TX.fromByte(result.tx.toByte()) - ?: Ble.BleState.TX.ZERO - ) - - emit( - Ble.Host( - info = newResult.copy( - rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) { - null - } else { - newResult.rssi - } - - ), - state = state, - hostState = tState - ) - ) - - } - - delay(1_000) - - } - - } + Ble.Accelerometer( + info = initialBle.updateBleInfo(), + state = initialBle.updateState(), + accelerometerState = tState ) } } - } + Result.success( + flow { + while (true){ - return Result.failure(BleException.UnexpectedResponse) + resultList[serial]?.let { ble -> + + firstResult.state = ble.updateState() + firstResult.info = ble.updateBleInfo() + + emit(when(firstResult){ + is Ble.Accelerometer -> { + Ble.Accelerometer( + info = ble.updateBleInfo(), + state = ble.updateState(), + accelerometerState = firstResult.accelerometerState + ) + } + is Ble.Beacon -> { + Ble.Beacon( + info = ble.updateBleInfo(), + state = ble.updateState() + ) + } + is Ble.Host -> { + Ble.Host( + info = ble.updateBleInfo(), + state = ble.updateState(), + hostState = firstResult.hostState + ) + } + is Ble.Thermometer -> { + Ble.Thermometer( + info = ble.updateBleInfo(), + state = ble.updateState(), + thermometerState = firstResult.thermometerState + ) + } + }) + + } + + delay(1_000) + + } + } + ) + + } } @@ -345,28 +279,25 @@ class BleRepositoryImpl @Inject constructor( try { - val service = connection.discoverServices() - .findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) + val service = connection.discoverServices().findService(serviceUUID) + ?: return Result.failure(BleException.UnexpectedResponse) var characteristic = service.findCharacteristic(temperatureReadUUID) ?: return Result.failure(BleException.UnexpectedResponse) characteristic.write(DataByteArray.from(1, 1)) + delay(2_000) + val temperature = characteristic.read().value.toUByteArray().toTemperature() characteristic = service.findCharacteristic(intervalReadUUID) ?: return Result.failure(BleException.UnexpectedResponse) - characteristic.write(DataByteArray.from(3, 0, 0, 0, )) - - val interval = characteristic.read().value.let { - if(it.size == 4){ - it.get4byteUIntAt(0).toLong() - }else{ - 0 - } - } + val interval = characteristic.readWriteInterval().fold( + onFailure = { return Result.failure(it) }, + onSuccess = { it } + ) connection.close() @@ -406,33 +337,31 @@ class BleRepositoryImpl @Inject constructor( try { - val service = connection.discoverServices() - .findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) + val service = connection.discoverServices().findService(serviceUUID) + ?: return Result.failure(BleException.UnexpectedResponse) val characteristic = service.findCharacteristic(intervalReadUUID) + ?: return Result.failure(BleException.UnexpectedResponse) - if(characteristic == null){ - service.characteristics.forEach { - Log.d("ble", "characteristic ${it.uuid} ") - } - Log.d("ble", "${intervalReadUUID} not found") - return Result.failure(BleException.UnexpectedResponse) - } + val interval = characteristic.readWriteInterval().fold( + onFailure = { return Result.failure(it) }, + onSuccess = { it } + ) + /*characteristic.write(DataByteArray.from(2, 0, 0, 0)) - characteristic.write(DataByteArray.from(3, 0, 0, 0)) - - val interval = characteristic.read().value.let { + val readTimeout = characteristic.read().value.let { if(it.size == 4){ it.get4byteUIntAt(0).toLong() - }else{ + } else { 0 } - } + }*/ return Result.success( Ble.Host.HostState( - historyInterval = interval + historyInterval = interval, + readInterval = interval ) ) @@ -453,6 +382,32 @@ class BleRepositoryImpl @Inject constructor( } + private suspend fun ClientBleGattCharacteristic.readWriteInterval( + + ): Result { + + return if(app.checkPermission().not()) { + + Result.failure(BleException.PermissionDenied) + + } else { + + write(DataByteArray.from(3, 0, 0, 0)) + + val interval = read().value.let { + if (it.size == 4) { + it.get4byteUIntAt(0).toLong() + } else { + 0 + } + } + + Result.success(interval) + + } + + } + private suspend fun readAccelState( address: String, timer: BleInfo.HistoryTableStatus @@ -471,15 +426,10 @@ class BleRepositoryImpl @Inject constructor( var characteristic = service.findCharacteristic(intervalReadUUID) ?: return Result.failure(BleException.UnexpectedResponse) - characteristic.write(DataByteArray.from(3, 0, 0, 0, )) - - val interval = characteristic.read().value.let { - if(it.size == 4){ - it.get4byteUIntAt(0).toLong() - } else { - 0 - } - } + val interval = characteristic.readWriteInterval().fold( + onFailure = { return Result.failure(it) }, + onSuccess = { it } + ) val historySettingsParams = when(timer){ BleInfo.HistoryTableStatus.EMPTY, @@ -561,7 +511,7 @@ class BleRepositoryImpl @Inject constructor( } override suspend fun addBleToHostTableBySerial(serial: String, ble: List): Result { - return editBleToHostTable(serial, ble, app) + return editBleHostTable(serial, ble, app) } override suspend fun getHostHistoryBySerial( @@ -747,6 +697,20 @@ class BleRepositoryImpl @Inject constructor( } + request.readInterval?.let { + + service.findCharacteristic(intervalWriteUUID)!!.write( + DataByteArray.from( + *mutableListOf(2).apply { + addAll( + (it / 1000).toUInt().to4ByteArrayInLittleEndian().reversed().toList() + ) + }.toByteArray() + ) + ) + + } + service.findCharacteristic( flashWriteUUID )!!.write( @@ -790,9 +754,9 @@ class BleRepositoryImpl @Inject constructor( try { val services = connection.discoverServices() - val service = services.findService(serviceUUID) ?: return Result.failure( - BleException.UnexpectedResponse - ) + + val service = services.findService(serviceUUID) + ?: return Result.failure(BleException.UnexpectedResponse) Log.d("write", request.toString()) diff --git a/app/src/main/java/llc/arma/ble/data/repository/ReadHostHistory.kt b/app/src/main/java/llc/arma/ble/data/repository/ReadHostHistory.kt index e41981f..6c031d0 100644 --- a/app/src/main/java/llc/arma/ble/data/repository/ReadHostHistory.kt +++ b/app/src/main/java/llc/arma/ble/data/repository/ReadHostHistory.kt @@ -1,8 +1,12 @@ package llc.arma.ble.data.repository +import android.Manifest import android.annotation.SuppressLint import android.app.Application +import android.content.pm.PackageManager import android.util.Log +import androidx.annotation.RequiresPermission +import androidx.core.app.ActivityCompat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -18,12 +22,11 @@ import llc.arma.ble.domain.model.Ble import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic -import okio.ByteString.Companion.decodeHex import java.nio.ByteBuffer import java.util.BitSet import java.util.Locale -@SuppressLint("MissingPermission") +@RequiresPermission(allOf = ["android.permission.BLUETOOTH_CONNECT"]) suspend fun readTable( characteristic: ClientBleGattCharacteristic, startRequest: ByteArray, @@ -172,66 +175,65 @@ fun readHostHistory( var periods = mutableListOf>>() var periodBle = mutableListOf() - var hasHit = false do { - val bleIdTableCell = firstTablePackage.drop(bleTableOffset).take(2).toByteArray() + val bleIdTableCell = + firstTablePackage.drop(bleTableOffset).take(2).toByteArray() - println("cell ${bleIdTableCell.toHexString()} ${bleIdTableCell.joinToString()}") + val intervalEnd = "ff0f" + val intervalEndAndHit = "fe0f" - if(bleIdTableCell.contentEquals(byteArrayOf(-1, 15)).not()) { - - if(bleIdTableCell.contentEquals(byteArrayOf(-2, 15))){ - bleTableOffset += 2 - hasHit = true - continue - } - - val innerIndex = getInnerIndex(bleIdTableCell[1]) - - val bleTableIndex = getBleIdIndex(bleIdTableCell) * 8u - - val serial = - secondTablePackage.drop(bleTableIndex.toInt()).take(6).reversed() - .joinToString( - separator = ":", - transform = { it.toHexString().padStart(2, '0') }) - .uppercase(Locale.getDefault()) - val devTypeByte = secondTablePackage.drop(bleTableIndex.toInt() + 6)[0] - - val devType = getDevType(devTypeByte) - val devDataSize = getDevDataSize(devTypeByte) - bleTableOffset += 2 - - if (devDataSize != 0) { - val payload = getBleIdIndex( - firstTablePackage.drop(bleTableOffset).take(devDataSize) - .toByteArray() - ) - bleTableOffset += devDataSize - } - - periodBle.add(serial) - - } else { + if (bleIdTableCell.contentEquals(intervalEnd.hexToByteArray())) { bleTableOffset += 2 - - } - - var nextIndex = 0 - - if(bleTableOffset <= firstTablePackage.size - 2){ - nextIndex = getInnerIndex(firstTablePackage.drop(bleTableOffset)[1]) - } - - if(nextIndex == 0){ - periods.add(Pair(hasHit, periodBle)) + if(periodBle.isEmpty()) bleTableOffset += 2 + periods.add(Pair(false, periodBle)) periodBle = mutableListOf() - hasHit = false + + continue + } + if (bleIdTableCell.contentEquals(intervalEndAndHit.hexToByteArray())) { + bleTableOffset += 2 + if(periodBle.isEmpty()) bleTableOffset += 2 + periods.add(Pair(true, periodBle)) + periodBle = mutableListOf() + + continue + + } + + val innerIndex = getInnerIndex(bleIdTableCell[1]) + + val bleTableIndex = getBleIdIndex(bleIdTableCell) * 8u + + val serial = + secondTablePackage.drop(bleTableIndex.toInt()).take(6).reversed() + .joinToString( + separator = ":", + transform = { it.toHexString().padStart(2, '0') }) + .uppercase(Locale.getDefault()) + val devTypeByte = secondTablePackage.drop(bleTableIndex.toInt() + 6)[0] + + val devType = getDevType(devTypeByte) + val devDataSize = getDevDataSize(devTypeByte) + bleTableOffset += 2 + + if(innerIndex == 0) + bleTableOffset += 2 + + if (devDataSize != 0) { + val payload = getBleIdIndex( + firstTablePackage.drop(bleTableOffset).take(devDataSize) + .toByteArray() + ) + bleTableOffset += devDataSize + } + + periodBle.add(serial) + } while (bleTableOffset < firstTablePackage.size) emit( @@ -290,7 +292,7 @@ suspend fun readHostBleTable( ?.findCharacteristic(hostHistoryReadUUID) ?: throw IllegalStateException() - characteristic.write(DataByteArray.from(2)) + characteristic.write(DataByteArray.from(7)) var value = characteristic.read().value @@ -346,7 +348,7 @@ suspend fun readHostBleTable( } @OptIn(ExperimentalStdlibApi::class) -suspend fun editBleToHostTable( +suspend fun editBleHostTable( address: String, addBleAddress: List, app: Application, @@ -366,7 +368,9 @@ suspend fun editBleToHostTable( characteristic.write(DataByteArray.from(12, 1)) - val writeCount = addBleAddress.chunked(40).sumOf { bleAddressBatch -> + Log.i("ScanRecord", "write") + + val writeCount = addBleAddress.chunked(20).sumOf { bleAddressBatch -> val countPayload = ByteBuffer.allocate(2).putShort(bleAddressBatch.size.toShort()).array().reversed().toByteArray() diff --git a/app/src/main/java/llc/arma/ble/data/repository/SettingsRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/repository/SettingsRepositoryImpl.kt index c99d5b9..53be460 100644 --- a/app/src/main/java/llc/arma/ble/data/repository/SettingsRepositoryImpl.kt +++ b/app/src/main/java/llc/arma/ble/data/repository/SettingsRepositoryImpl.kt @@ -7,7 +7,6 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore -import com.google.gson.Gson import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.serialization.decodeFromString diff --git a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt index c2c45b8..4672a54 100644 --- a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt +++ b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt @@ -4,14 +4,15 @@ import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode sealed class Ble( - val info: BleInfo + var info: BleInfo, + var state: BleState, ) { class Accelerometer( info: BleInfo, - val state: BleState, + state: BleState, val accelerometerState: AccelerometerState - ): Ble(info) { + ): Ble(info, state) { sealed class HistorySettings { @@ -99,8 +100,8 @@ sealed class Ble( class Beacon( info: BleInfo, - val state: BleState - ) : Ble(info){ + state: BleState, + ) : Ble(info, state){ data class WriteRequest( val tx: BleState.TX? @@ -110,9 +111,9 @@ sealed class Ble( class Host( info: BleInfo, - val state: BleState, + state: BleState, val hostState: HostState - ) : Ble(info){ + ) : Ble(info, state){ class HistoryPoint( val date: Long, @@ -121,21 +122,23 @@ sealed class Ble( ) data class HostState( - val historyInterval: Long + val historyInterval: Long, + val readInterval: Long ) data class WriteRequest( val tx: BleState.TX?, - val interval: Long? + val interval: Long?, + val readInterval: Long?, ) } class Thermometer( info: BleInfo, - val state: BleState, + state: BleState, val thermometerState: ThermometerState - ) : Ble(info) { + ) : Ble(info, state) { class HistoryPoint( val date: Long,