rotations save
This commit is contained in:
parent
55b6807081
commit
549b625820
|
|
@ -96,6 +96,10 @@ dependencies {
|
|||
implementation "com.patrykandpatrick.vico:compose:1.7.1"
|
||||
implementation "com.patrykandpatrick.vico:compose-m3:1.7.1"
|
||||
|
||||
implementation "androidx.room:room-runtime:2.6.1"
|
||||
kapt("androidx.room:room-compiler:2.6.1")
|
||||
implementation("androidx.room:room-ktx:2.6.1")
|
||||
|
||||
implementation files('libs/poishadow-all.jar')
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package llc.arma.ble.app.framework.di
|
||||
|
||||
import android.app.Application
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import llc.arma.ble.data.db.AppDatabase
|
||||
import llc.arma.ble.data.db.RotationsDao
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class DatabaseModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDatabase(app: Application): AppDatabase {
|
||||
return Room.databaseBuilder(app, AppDatabase::class.java, "app-database").build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRotationsDao(db: AppDatabase): RotationsDao {
|
||||
return db.getRotationsDao()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,11 +4,13 @@ import dagger.Binds
|
|||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import llc.arma.ble.data.BleRepositoryImpl
|
||||
import llc.arma.ble.data.EmailRepositoryImpl
|
||||
import llc.arma.ble.data.XlsxRepositoryImpl
|
||||
import llc.arma.ble.data.repository.BleRepositoryImpl
|
||||
import llc.arma.ble.data.repository.EmailRepositoryImpl
|
||||
import llc.arma.ble.data.repository.RotationsRepositoryImpl
|
||||
import llc.arma.ble.data.repository.XlsxRepositoryImpl
|
||||
import llc.arma.ble.domain.repository.BleRepository
|
||||
import llc.arma.ble.domain.repository.EmailRepository
|
||||
import llc.arma.ble.domain.repository.RotationsRepository
|
||||
import llc.arma.ble.domain.repository.XlsxRepository
|
||||
|
||||
@Module
|
||||
|
|
@ -21,6 +23,9 @@ interface RepositoryBinding {
|
|||
@Binds
|
||||
fun bindEmailRepository(repository: EmailRepositoryImpl): EmailRepository
|
||||
|
||||
@Binds
|
||||
fun bindRotationsRepository(repository: RotationsRepositoryImpl): RotationsRepository
|
||||
|
||||
@Binds
|
||||
fun bindXlsxRepository(repository: XlsxRepositoryImpl): XlsxRepository
|
||||
|
||||
|
|
|
|||
|
|
@ -7,15 +7,19 @@ import android.bluetooth.BluetoothAdapter
|
|||
import android.bluetooth.BluetoothManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.hardware.camera2.CameraCharacteristics
|
||||
import android.hardware.camera2.CameraManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.SurfaceView
|
||||
import android.view.View
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
|
|
@ -28,8 +32,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.ActivityCompat.startActivityForResult
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
|
|
@ -231,19 +234,20 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun OnLifecycleEvent(onEvent: (owner: LifecycleOwner, event: Lifecycle.Event) -> Unit) {
|
||||
val eventHandler = rememberUpdatedState(onEvent)
|
||||
val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)
|
||||
fun Camera(){
|
||||
|
||||
DisposableEffect(lifecycleOwner.value) {
|
||||
val lifecycle = lifecycleOwner.value.lifecycle
|
||||
val observer = LifecycleEventObserver { owner, event ->
|
||||
eventHandler.value(owner, event)
|
||||
}
|
||||
AndroidView(
|
||||
factory = {
|
||||
val cameraManager = it.getSystemService(CameraManager::class.java)
|
||||
|
||||
lifecycle.addObserver(observer)
|
||||
onDispose {
|
||||
lifecycle.removeObserver(observer)
|
||||
cameraManager.cameraIdList.forEach {
|
||||
|
||||
println("$it is front: ${cameraManager.getCameraCharacteristics(it).get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT}")
|
||||
|
||||
}
|
||||
|
||||
return@AndroidView SurfaceView(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -66,6 +66,10 @@ class ConnectionContract {
|
|||
val serial: String
|
||||
) : Navigation()
|
||||
|
||||
data class NavigateToRotationsStatistic(
|
||||
val serial: String
|
||||
) : Navigation()
|
||||
|
||||
}
|
||||
|
||||
sealed class InnerNavigation : Effect() {
|
||||
|
|
|
|||
|
|
@ -184,7 +184,9 @@ fun ConnectionScreen(
|
|||
onDismiss = {
|
||||
innerScreen = null
|
||||
}
|
||||
)
|
||||
){
|
||||
onNavigationEvent(ConnectionContract.Effect.Navigation.NavigateToRotationsStatistic(it.ble.serial))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.rounded.CloudUpload
|
||||
import androidx.compose.material.icons.rounded.Refresh
|
||||
import androidx.compose.material.icons.rounded.TableView
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.patrykandpatrick.vico.compose.axis.axisGuidelineComponent
|
||||
|
|
@ -71,6 +72,7 @@ class AccelEntry(
|
|||
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AccelerometerHistory(
|
||||
ble: BleInfo,
|
||||
|
|
@ -79,7 +81,8 @@ fun AccelerometerHistory(
|
|||
fftAxis: FftAxis,
|
||||
fftMode: FftViewMode,
|
||||
frequency: FftFrequency,
|
||||
onDismiss: (() -> Unit)? = null
|
||||
onDismiss: (() -> Unit)? = null,
|
||||
onShowStatistic: () -> Unit,
|
||||
) {
|
||||
|
||||
val viewModel = hiltViewModel<AccelerometerHistoryViewModel>()
|
||||
|
|
@ -95,74 +98,85 @@ fun AccelerometerHistory(
|
|||
}
|
||||
}*/
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxHeight(0.9f)
|
||||
) {
|
||||
Column() {
|
||||
|
||||
Row(
|
||||
modifier = Modifier.padding(horizontal = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
onDismiss?.let {
|
||||
|
||||
onDismiss?.let {
|
||||
IconButton(onClick = it) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.ArrowBack,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = it) {
|
||||
}
|
||||
},
|
||||
title = {
|
||||
val title = when(state){
|
||||
is AccelerometerHistoryContract.State.Display -> {
|
||||
when (state.loadingHistoryState) {
|
||||
is ProgressState.Finished -> "${accelMode.localized} (${state.loadingHistoryState.data.size})"
|
||||
is ProgressState.Indeterminate -> accelMode.localized
|
||||
is ProgressState.Progress -> accelMode.localized
|
||||
}
|
||||
}
|
||||
AccelerometerHistoryContract.State.Exception -> accelMode.localized
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
|
||||
IconButton(
|
||||
onClick = onShowStatistic,
|
||||
enabled = when(state){
|
||||
is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
|
||||
AccelerometerHistoryContract.State.Exception -> false
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.ArrowBack,
|
||||
imageVector = Icons.Rounded.TableView,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val title = when(state){
|
||||
is AccelerometerHistoryContract.State.Display -> {
|
||||
when (state.loadingHistoryState) {
|
||||
is ProgressState.Finished -> "${accelMode.localized} (${state.loadingHistoryState.data.size})"
|
||||
is ProgressState.Indeterminate -> accelMode.localized
|
||||
is ProgressState.Progress -> accelMode.localized
|
||||
IconButton(
|
||||
onClick = {
|
||||
viewModel.setEvent(AccelerometerHistoryContract.Event.OnExport)
|
||||
},
|
||||
enabled = when(state){
|
||||
is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
|
||||
AccelerometerHistoryContract.State.Exception -> false
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.CloudUpload,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
AccelerometerHistoryContract.State.Exception -> accelMode.localized
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
|
||||
IconButton(
|
||||
onClick = {
|
||||
viewModel.setEvent(AccelerometerHistoryContract.Event.OnExport)
|
||||
},
|
||||
enabled = when(state){
|
||||
is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
|
||||
AccelerometerHistoryContract.State.Exception -> false
|
||||
IconButton(
|
||||
onClick = {
|
||||
viewModel.setEvent(AccelerometerHistoryContract.Event.OnRefreshHistory(ble.name, ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency))
|
||||
},
|
||||
enabled = when(state){
|
||||
is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
|
||||
AccelerometerHistoryContract.State.Exception -> true
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Refresh,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.CloudUpload,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(
|
||||
onClick = {
|
||||
viewModel.setEvent(AccelerometerHistoryContract.Event.OnRefreshHistory(ble.name, ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency))
|
||||
},
|
||||
enabled = when(state){
|
||||
is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
|
||||
AccelerometerHistoryContract.State.Exception -> true
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Refresh,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ import llc.arma.ble.app.ui.screen.connection.ConnectionContract
|
|||
import llc.arma.ble.app.ui.screen.connection.ConnectionScreen
|
||||
import llc.arma.ble.app.ui.screen.password.ChangePasswordContract
|
||||
import llc.arma.ble.app.ui.screen.password.ChangePasswordScreen
|
||||
import llc.arma.ble.app.ui.screen.rotation.delete.RotationDeleteContract
|
||||
import llc.arma.ble.app.ui.screen.rotation.delete.RotationDeleteScreen
|
||||
import llc.arma.ble.app.ui.screen.rotation.statistic.RotationStatisticContract
|
||||
import llc.arma.ble.app.ui.screen.rotation.statistic.RotationStatisticScreen
|
||||
|
||||
@Composable
|
||||
fun MainScreen() {
|
||||
|
|
@ -49,7 +53,10 @@ fun MainScreen() {
|
|||
onNavigationEvent = {
|
||||
when(it){
|
||||
ConnectionContract.Effect.Navigation.NavigateUp -> controller.navigateUp()
|
||||
is ConnectionContract.Effect.Navigation.NavigateToChangePassword -> controller.navigate("change_password/${it.serial}")
|
||||
is ConnectionContract.Effect.Navigation.NavigateToChangePassword ->
|
||||
controller.navigate("change_password/${it.serial}")
|
||||
is ConnectionContract.Effect.Navigation.NavigateToRotationsStatistic ->
|
||||
controller.navigate("rotation_statistic/${it.serial}")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -57,6 +64,36 @@ fun MainScreen() {
|
|||
}
|
||||
)
|
||||
|
||||
composable(
|
||||
route = "rotation_statistic/{serial}",
|
||||
content = {
|
||||
|
||||
RotationStatisticScreen {
|
||||
when(it){
|
||||
is RotationStatisticContract.Effect.Navigation.NavigateToDelete -> {
|
||||
controller.navigate("rotation_delete/${it.serial}")
|
||||
}
|
||||
RotationStatisticContract.Effect.Navigation.NavigateUp -> {
|
||||
controller.navigateUp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
dialog(
|
||||
route = "rotation_delete/{serial}",
|
||||
dialogProperties = DialogProperties(usePlatformDefaultWidth = false),
|
||||
content = {
|
||||
RotationDeleteScreen {
|
||||
when(it){
|
||||
RotationDeleteContract.Effect.Navigation.NavigateUp -> controller.navigateUp()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
dialog(
|
||||
route = "change_password/{serial}",
|
||||
dialogProperties = DialogProperties(usePlatformDefaultWidth = false),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package llc.arma.ble.app.ui.screen.rotation.delete
|
||||
|
||||
import llc.arma.ble.app.ui.common.ViewEvent
|
||||
import llc.arma.ble.app.ui.common.ViewSideEffect
|
||||
import llc.arma.ble.app.ui.common.ViewState
|
||||
import llc.arma.ble.domain.model.Rotation
|
||||
|
||||
class RotationDeleteContract {
|
||||
|
||||
sealed class Event : ViewEvent {
|
||||
|
||||
object OnNavigateUp : Event()
|
||||
|
||||
object OnDelete : Event()
|
||||
|
||||
}
|
||||
|
||||
object State : ViewState
|
||||
|
||||
sealed class Effect : ViewSideEffect {
|
||||
|
||||
sealed class Navigation : Effect(){
|
||||
|
||||
object NavigateUp : Navigation()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
package llc.arma.ble.app.ui.screen.rotation.delete
|
||||
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
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 androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import llc.arma.ble.app.ui.screen.rotation.statistic.RotationStatisticContract
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun RotationDeleteScreen(
|
||||
onNavigation: (RotationDeleteContract.Effect.Navigation) -> Unit
|
||||
) {
|
||||
|
||||
val viewModel = hiltViewModel<RotationDeleteViewModel>()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.effect.onEach {
|
||||
when(it){
|
||||
is RotationDeleteContract.Effect.Navigation -> onNavigation(it)
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
|
||||
Text(
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
text = "Удалить сохраненные записи?"
|
||||
)
|
||||
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
|
||||
modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
) {
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.setEvent(
|
||||
RotationDeleteContract.Event.OnNavigateUp
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(text = "Отмена")
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.setEvent(
|
||||
RotationDeleteContract.Event.OnDelete
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(text = "Удалить")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package llc.arma.ble.app.ui.screen.rotation.delete
|
||||
|
||||
import android.icu.util.GregorianCalendar
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import llc.arma.ble.app.ui.common.BaseViewModel
|
||||
import llc.arma.ble.domain.usecase.DeleteRotationsBySerial
|
||||
import llc.arma.ble.domain.usecase.GetRotationsBySerial
|
||||
import llc.arma.ble.domain.usecase.GetWheelRadiusBySerial
|
||||
import llc.arma.ble.domain.usecase.SetWheelRadius
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class RotationDeleteViewModel @Inject constructor(
|
||||
private val savedStateHandle: SavedStateHandle,
|
||||
private val deleteRotationsBySerial: DeleteRotationsBySerial
|
||||
) : BaseViewModel<RotationDeleteContract.State, RotationDeleteContract.Event, RotationDeleteContract.Effect>() {
|
||||
|
||||
override fun setInitialState() = RotationDeleteContract.State
|
||||
|
||||
override fun handleEvents(event: RotationDeleteContract.Event) {
|
||||
when(event){
|
||||
is RotationDeleteContract.Event.OnDelete -> reduce(viewState.value, event)
|
||||
is RotationDeleteContract.Event.OnNavigateUp -> reduce(viewState.value, event)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun reduce(
|
||||
state: RotationDeleteContract.State,
|
||||
event: RotationDeleteContract.Event.OnNavigateUp
|
||||
) {
|
||||
|
||||
setEffect {
|
||||
|
||||
RotationDeleteContract.Effect.Navigation.NavigateUp
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: RotationDeleteContract.State,
|
||||
event: RotationDeleteContract.Event.OnDelete
|
||||
) {
|
||||
|
||||
viewModelScope.launch {
|
||||
|
||||
deleteRotationsBySerial(savedStateHandle.get<String>("serial")!!)
|
||||
|
||||
}
|
||||
|
||||
setEffect {
|
||||
|
||||
RotationDeleteContract.Effect.Navigation.NavigateUp
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package llc.arma.ble.app.ui.screen.rotation.statistic
|
||||
|
||||
import llc.arma.ble.app.ui.common.ViewEvent
|
||||
import llc.arma.ble.app.ui.common.ViewSideEffect
|
||||
import llc.arma.ble.app.ui.common.ViewState
|
||||
import llc.arma.ble.domain.model.Rotation
|
||||
|
||||
class RotationStatisticContract {
|
||||
|
||||
sealed class Event : ViewEvent {
|
||||
|
||||
object OnNavigateUp : Event()
|
||||
|
||||
object OnNavigateToDelete : Event()
|
||||
|
||||
data class OnRadiusChanged(
|
||||
val radius: Long?
|
||||
): Event()
|
||||
|
||||
data class OnStartDateChanged(
|
||||
val start: Long?
|
||||
): Event()
|
||||
|
||||
data class OnStopDateChanged(
|
||||
val stop: Long?
|
||||
): Event()
|
||||
|
||||
}
|
||||
|
||||
sealed class State : ViewState {
|
||||
|
||||
object Loading : State()
|
||||
|
||||
data class Display(
|
||||
val rotations: List<Rotation>,
|
||||
val ranges: List<RotationRange>,
|
||||
val radius: Long?,
|
||||
val startDate: Long?,
|
||||
val endDate: Long?
|
||||
) : State() {
|
||||
|
||||
data class RotationRange(
|
||||
val distance: Double,
|
||||
val startDate: Long,
|
||||
val endDate: Long,
|
||||
val maxSpeed: Double,
|
||||
val avgSpeed: Double,
|
||||
val rotations: Float,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sealed class Effect : ViewSideEffect {
|
||||
|
||||
sealed class Navigation : Effect(){
|
||||
|
||||
object NavigateUp : Navigation()
|
||||
|
||||
data class NavigateToDelete(
|
||||
val serial: String
|
||||
) : Navigation()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,496 @@
|
|||
package llc.arma.ble.app.ui.screen.rotation.statistic
|
||||
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.rounded.ArrowForward
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
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 androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RotationStatisticScreen(
|
||||
onNavigation: (RotationStatisticContract.Effect.Navigation) -> Unit
|
||||
) {
|
||||
|
||||
val viewModel = hiltViewModel<RotationStatisticViewModel>()
|
||||
val state = viewModel.viewState.value
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.effect.onEach {
|
||||
when(it){
|
||||
is RotationStatisticContract.Effect.Navigation -> onNavigation(it)
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
Column(
|
||||
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
viewModel.setEvent(RotationStatisticContract.Event.OnNavigateUp)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.ArrowBack,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = "Обороты",
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
viewModel.setEvent(RotationStatisticContract.Event.OnNavigateToDelete)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Delete,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.padding(12.dp)
|
||||
) {
|
||||
|
||||
if(state is RotationStatisticContract.State.Display){
|
||||
|
||||
var showStartDateSelector by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
var showEndDateSelector by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.width(IntrinsicSize.Max)
|
||||
.height(IntrinsicSize.Max)
|
||||
) {
|
||||
|
||||
TextField(
|
||||
readOnly = true,
|
||||
value = dateFormat.format(Date(state.startDate ?: state.ranges.minOfOrNull { it.startDate } ?: 0)),
|
||||
onValueChange = {},
|
||||
label = {
|
||||
Text(text = "Начало")
|
||||
},
|
||||
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickable { showStartDateSelector = true }
|
||||
) {}
|
||||
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.width(IntrinsicSize.Max)
|
||||
.height(IntrinsicSize.Max)
|
||||
) {
|
||||
|
||||
TextField(
|
||||
readOnly = true,
|
||||
value = dateFormat.format(Date(state.endDate ?: state.ranges.maxOfOrNull { it.endDate } ?: 0)),
|
||||
onValueChange = {},
|
||||
label = {
|
||||
Text(text = "Завершение")
|
||||
}
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clickable { showEndDateSelector = true }
|
||||
) {}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TextField(
|
||||
singleLine = true,
|
||||
value = state.radius?.toString() ?: "",
|
||||
onValueChange = {
|
||||
viewModel.setEvent(
|
||||
RotationStatisticContract.Event.OnRadiusChanged(
|
||||
it.filter { it.isDigit() }.toLongOrNull()
|
||||
)
|
||||
)
|
||||
},
|
||||
label = {
|
||||
Text(text = "Радиус колеса (мм.)")
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
|
||||
Column {
|
||||
|
||||
Text(text = "Пройденное расстояние")
|
||||
Text(text = "Движение вперед")
|
||||
Text(text = "Движение назад")
|
||||
Text(text = "Стоянка")
|
||||
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
val duration = state.ranges.filter { it.distance == 0.0 }.sumOf { it.endDate - it.startDate }
|
||||
val hours = duration / TimeUnit.HOURS.toMillis(1)
|
||||
val minutes = (duration - (hours * TimeUnit.HOURS.toMillis(1))) / TimeUnit.MINUTES.toMillis(1)
|
||||
val seconds = (duration - (hours * TimeUnit.HOURS.toMillis(1)) - (minutes * TimeUnit.MINUTES.toMillis(1))) / TimeUnit.SECONDS.toMillis(1)
|
||||
|
||||
Text(text = state.ranges.sumOf { it.distance.absoluteValue }.roundTo().toString() + " м.")
|
||||
Text(text = state.ranges.filter { it.distance > 0 }.sumOf { it.distance.absoluteValue }.roundTo().toString() + " м.")
|
||||
Text(text = state.ranges.filter { it.distance < 0 }.sumOf { it.distance.absoluteValue }.roundTo().toString() + " м.")
|
||||
Text(text = "${hours}ч ${minutes}м. ${seconds}с.")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var reversed by remember {
|
||||
mutableStateOf(true)
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
|
||||
Text(text = "Порядок")
|
||||
|
||||
IconButton(
|
||||
onClick = { reversed = reversed.not() }
|
||||
) {
|
||||
|
||||
Icon(
|
||||
imageVector = if (reversed) Icons.Rounded.ArrowBack else Icons.Rounded.ArrowForward,
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LazyRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
|
||||
items(items = state.ranges.let { if (reversed) it.reversed() else it}){
|
||||
DataRange(range = it)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(showStartDateSelector) {
|
||||
|
||||
Dialog(
|
||||
properties = DialogProperties(usePlatformDefaultWidth = false),
|
||||
onDismissRequest = {
|
||||
showEndDateSelector = false
|
||||
}
|
||||
) {
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
|
||||
val datePickerState = rememberDatePickerState(
|
||||
initialSelectedDateMillis = state.startDate
|
||||
)
|
||||
|
||||
Column{
|
||||
|
||||
DatePicker(state = datePickerState)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
|
||||
modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
.padding(12.dp)
|
||||
) {
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
showStartDateSelector = false
|
||||
}
|
||||
) {
|
||||
Text(text = "Отмена")
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.setEvent(
|
||||
RotationStatisticContract.Event.OnStartDateChanged(
|
||||
datePickerState.selectedDateMillis ?: state.startDate
|
||||
)
|
||||
)
|
||||
showStartDateSelector = false
|
||||
}
|
||||
) {
|
||||
Text(text = "ок")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(showEndDateSelector) {
|
||||
|
||||
Dialog(
|
||||
properties = DialogProperties(usePlatformDefaultWidth = false),
|
||||
onDismissRequest = {
|
||||
showEndDateSelector = false
|
||||
}
|
||||
) {
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
) {
|
||||
|
||||
val datePickerState = rememberDatePickerState(
|
||||
initialSelectedDateMillis = state.endDate
|
||||
)
|
||||
|
||||
Column {
|
||||
|
||||
DatePicker(state = datePickerState)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
|
||||
modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
.padding(12.dp)
|
||||
) {
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
showEndDateSelector = false
|
||||
}
|
||||
) {
|
||||
Text(text = "Отмена")
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.setEvent(
|
||||
RotationStatisticContract.Event.OnStopDateChanged(
|
||||
datePickerState.selectedDateMillis ?: state.endDate
|
||||
)
|
||||
)
|
||||
showEndDateSelector = false
|
||||
}
|
||||
) {
|
||||
Text(text = "ок")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Double.roundTo(numFractionDigits: Int = 2): Double {
|
||||
val factor = 10.0.pow(numFractionDigits.toDouble())
|
||||
return (this * factor).roundToInt() / factor
|
||||
}
|
||||
|
||||
private fun Double.round(decimals: Int = 2): Double = "%.${decimals}f".format(this).toDouble()
|
||||
|
||||
private val dateTimeFormat = SimpleDateFormat("dd.MM HH:mm:ss")
|
||||
private val dateFormat = SimpleDateFormat("dd.MM HH:mm:ss")
|
||||
|
||||
@Composable
|
||||
private fun DataRange(range: RotationStatisticContract.State.Display.RotationRange){
|
||||
|
||||
Card {
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(12.dp)
|
||||
.width(IntrinsicSize.Max)
|
||||
) {
|
||||
|
||||
val duration = range.endDate - range.startDate
|
||||
val hours = duration / TimeUnit.HOURS.toMillis(1)
|
||||
val minutes = (duration - (hours * TimeUnit.HOURS.toMillis(1))) / TimeUnit.MINUTES.toMillis(1)
|
||||
val seconds = (duration - (hours * TimeUnit.HOURS.toMillis(1)) - (minutes * TimeUnit.MINUTES.toMillis(1))) / TimeUnit.SECONDS.toMillis(1)
|
||||
|
||||
val directionText = if(range.distance > 0){
|
||||
"-->"
|
||||
}else{
|
||||
if(range.distance < 0) {
|
||||
"<--"
|
||||
} else {
|
||||
"<<P>>"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
|
||||
Column {
|
||||
|
||||
Text(text = "Начало")
|
||||
Text(text = "Конец")
|
||||
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
Text(text = dateTimeFormat.format(Date(range.startDate)))
|
||||
Text(text = dateTimeFormat.format(Date(range.endDate)))
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text(text = "Направление: " + directionText)
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
|
||||
Column {
|
||||
|
||||
Text(text = "Время")
|
||||
|
||||
if(range.distance != 0.0) {
|
||||
|
||||
Text(text = "Расстояние")
|
||||
Text(text = "Vmax.")
|
||||
Text(text = "Vср.")
|
||||
Text(text = "Обороты")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Column {
|
||||
|
||||
Text(text = "${hours}ч ${minutes}м. ${seconds}с.")
|
||||
|
||||
if(range.distance != 0.0) {
|
||||
|
||||
Text(text = range.distance.absoluteValue.roundTo().toString() + " м.")
|
||||
Text(text = range.maxSpeed.roundTo().toString() + " Км/ч")
|
||||
Text(text = range.avgSpeed.roundTo().toString() + " Км/ч")
|
||||
Text(text = range.rotations.absoluteValue.toDouble().roundTo().toString())
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
package llc.arma.ble.app.ui.screen.rotation.statistic
|
||||
|
||||
import android.icu.util.GregorianCalendar
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.patrykandpatrick.vico.core.extension.sumOf
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import llc.arma.ble.app.ui.common.BaseViewModel
|
||||
import llc.arma.ble.domain.usecase.GetRotationsBySerial
|
||||
import llc.arma.ble.domain.usecase.GetWheelRadiusBySerial
|
||||
import llc.arma.ble.domain.usecase.SetWheelRadius
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
fun <T> Collection<T>.splitBy(separator: (T) -> Any?): List<List<T>> {
|
||||
|
||||
val result = mutableListOf<MutableList<T>>()
|
||||
|
||||
var newSublist = true
|
||||
for (item in this) {
|
||||
newSublist = (separator(item) != result.lastOrNull()?.lastOrNull()?.let { separator(it) })
|
||||
if (newSublist || result.lastOrNull() == null)
|
||||
result += mutableListOf<T>()
|
||||
result.last() += item
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@HiltViewModel
|
||||
class RotationStatisticViewModel @Inject constructor(
|
||||
private val setWheelRadius: SetWheelRadius,
|
||||
private val getWheelRadius: GetWheelRadiusBySerial,
|
||||
private val getRotationsBySerial: GetRotationsBySerial,
|
||||
private val savedStateHandle: SavedStateHandle
|
||||
) : BaseViewModel<RotationStatisticContract.State, RotationStatisticContract.Event, RotationStatisticContract.Effect>() {
|
||||
|
||||
init {
|
||||
|
||||
viewModelScope.launch {
|
||||
|
||||
load()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun setInitialState() = RotationStatisticContract.State.Loading
|
||||
|
||||
override fun handleEvents(event: RotationStatisticContract.Event) {
|
||||
when(event){
|
||||
is RotationStatisticContract.Event.OnRadiusChanged -> reduce(viewState.value, event)
|
||||
is RotationStatisticContract.Event.OnStartDateChanged -> reduce(viewState.value, event)
|
||||
is RotationStatisticContract.Event.OnStopDateChanged -> reduce(viewState.value, event)
|
||||
is RotationStatisticContract.Event.OnNavigateToDelete -> reduce(viewState.value, event)
|
||||
is RotationStatisticContract.Event.OnNavigateUp -> reduce(viewState.value, event)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun load(){
|
||||
savedStateHandle.get<String>("serial")?.let {
|
||||
|
||||
val state = viewState.value
|
||||
|
||||
val r = getWheelRadius(it)?.toFloat() ?: 500f
|
||||
val rCm = r / 10f
|
||||
val circumference = 2 * Math.PI * rCm
|
||||
Log.d("circumference", circumference.toString())
|
||||
|
||||
var rotations = getRotationsBySerial(it)
|
||||
|
||||
if(state is RotationStatisticContract.State.Display){
|
||||
rotations = rotations.filter {
|
||||
it.date >= (state.startDate ?: 0) && it.date <= (state.endDate ?: Long.MAX_VALUE)
|
||||
}
|
||||
}
|
||||
|
||||
val split: List<RotationStatisticContract.State.Display.RotationRange> = if(rotations.size < 2) {
|
||||
emptyList()
|
||||
}else {
|
||||
|
||||
val pointDuration = rotations[1].date - rotations[0].date
|
||||
|
||||
rotations.splitBy {
|
||||
Log.d("rDate", it.date.toString())
|
||||
if (it.rotations > 0) {
|
||||
true
|
||||
} else {
|
||||
if (it.rotations < 0) {
|
||||
false
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}.map { range ->
|
||||
|
||||
val start = range.minOf { it.date } - pointDuration
|
||||
val end = range.maxOf { it.date }
|
||||
|
||||
val speeds = range.map {
|
||||
val km = ((it.rotations.absoluteValue / 8f) * circumference) / 100_000f
|
||||
km / (pointDuration.toFloat() / TimeUnit.HOURS.toMillis(1).toFloat())
|
||||
}
|
||||
|
||||
RotationStatisticContract.State.Display.RotationRange(
|
||||
distance = ((range.sumOf { it.rotations.toFloat() } / 8f) * circumference) / 100f,
|
||||
startDate = start,
|
||||
endDate = end,
|
||||
maxSpeed = speeds.max(),
|
||||
avgSpeed = speeds.average(),
|
||||
rotations = range.sumOf { it.rotations.toFloat() } / 8f
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setState {
|
||||
|
||||
when(this){
|
||||
is RotationStatisticContract.State.Display -> {
|
||||
copy(
|
||||
rotations = rotations,
|
||||
ranges = split
|
||||
)
|
||||
}
|
||||
RotationStatisticContract.State.Loading -> {
|
||||
RotationStatisticContract.State.Display(
|
||||
rotations = rotations,
|
||||
ranges = split,
|
||||
radius = r.toLong(),
|
||||
startDate = null,
|
||||
endDate = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} ?: throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: RotationStatisticContract.State,
|
||||
event: RotationStatisticContract.Event.OnRadiusChanged
|
||||
) {
|
||||
|
||||
if(state is RotationStatisticContract.State.Display){
|
||||
|
||||
setState {
|
||||
state.copy(radius = event.radius)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
|
||||
setWheelRadius(savedStateHandle.get<String>("serial")!!, event.radius ?: 100L)
|
||||
|
||||
load()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: RotationStatisticContract.State,
|
||||
event: RotationStatisticContract.Event.OnStartDateChanged
|
||||
) {
|
||||
|
||||
if(state is RotationStatisticContract.State.Display){
|
||||
|
||||
val mCalendar = GregorianCalendar()
|
||||
val mTimeZone = mCalendar.timeZone
|
||||
val mGMTOffset: Int = mTimeZone.rawOffset
|
||||
|
||||
val start = event.start?.let {
|
||||
TimeUnit.DAYS.toMillis(TimeUnit.MILLISECONDS.toDays(event.start)) - mGMTOffset
|
||||
}
|
||||
|
||||
Log.d("date", start.toString() + " " + event.start)
|
||||
|
||||
setState {
|
||||
state.copy(startDate = start)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
|
||||
load()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: RotationStatisticContract.State,
|
||||
event: RotationStatisticContract.Event.OnStopDateChanged
|
||||
) {
|
||||
|
||||
if(state is RotationStatisticContract.State.Display){
|
||||
|
||||
val mCalendar = GregorianCalendar()
|
||||
val mTimeZone = mCalendar.timeZone
|
||||
val mGMTOffset: Int = mTimeZone.rawOffset
|
||||
|
||||
val stop = event.stop?.let {
|
||||
TimeUnit.DAYS.toMillis(TimeUnit.MILLISECONDS.toDays(event.stop) + 1) - mGMTOffset - 1_000
|
||||
}
|
||||
|
||||
setState {
|
||||
state.copy(endDate = stop)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
|
||||
load()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: RotationStatisticContract.State,
|
||||
event: RotationStatisticContract.Event.OnNavigateUp
|
||||
) {
|
||||
|
||||
setEffect {
|
||||
|
||||
RotationStatisticContract.Effect.Navigation.NavigateUp
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: RotationStatisticContract.State,
|
||||
event: RotationStatisticContract.Event.OnNavigateToDelete
|
||||
) {
|
||||
|
||||
setEffect {
|
||||
|
||||
RotationStatisticContract.Effect.Navigation.NavigateToDelete(savedStateHandle.get<String>("serial")!!)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package llc.arma.ble.data.db
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import llc.arma.ble.data.model.RotationEntity
|
||||
import llc.arma.ble.data.model.WheelEntity
|
||||
|
||||
@Database(
|
||||
entities = [RotationEntity::class, WheelEntity::class],
|
||||
version = 1
|
||||
)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun getRotationsDao(): RotationsDao
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package llc.arma.ble.data.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import llc.arma.ble.data.model.RotationEntity
|
||||
import llc.arma.ble.data.model.WheelEntity
|
||||
|
||||
@Dao
|
||||
interface RotationsDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun save(rotations: WheelEntity)
|
||||
|
||||
@Query("select * from wheel where bleId = :serial")
|
||||
suspend fun getWheelBySerial(serial: String): WheelEntity?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun save(rotations: List<RotationEntity>)
|
||||
|
||||
@Query("delete from rotation where bleId = :serial")
|
||||
suspend fun deleteBySerial(serial: String)
|
||||
|
||||
@Query("delete from rotation where bleId = :serial and date > :date")
|
||||
suspend fun deleteBySerialAndDateMoreThan(serial: String, date: Long)
|
||||
|
||||
@Query("select * from rotation where bleId = :serial order by date asc")
|
||||
suspend fun getBySerialSortedByDate(serial: String): List<RotationEntity>
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package llc.arma.ble.data.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
|
||||
@Entity(
|
||||
tableName = "rotation",
|
||||
indices = [Index(unique = true, value = ["bleId", "date"])],
|
||||
primaryKeys = ["bleId", "date"]
|
||||
)
|
||||
class RotationEntity(
|
||||
|
||||
val bleId: String,
|
||||
|
||||
val date: Long,
|
||||
|
||||
val rotations: Long
|
||||
|
||||
)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package llc.arma.ble.data.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "wheel",
|
||||
indices = [Index(unique = true, value = ["bleId"])]
|
||||
)
|
||||
class WheelEntity(
|
||||
|
||||
@PrimaryKey
|
||||
val bleId: String,
|
||||
|
||||
val radius: Long
|
||||
|
||||
)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package llc.arma.ble.data
|
||||
package llc.arma.ble.data.repository
|
||||
|
||||
import android.app.Application
|
||||
import android.os.SystemClock
|
||||
|
|
@ -14,12 +14,13 @@ import kotlinx.coroutines.flow.flow
|
|||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import llc.arma.ble.data.extensions.checkPermission
|
||||
import llc.arma.ble.data.extensions.fromByte
|
||||
import llc.arma.ble.data.extensions.get4byteUIntAt
|
||||
import llc.arma.ble.data.extensions.info
|
||||
import llc.arma.ble.data.extensions.sendData
|
||||
import llc.arma.ble.data.extensions.toTemperature
|
||||
import llc.arma.ble.data.db.RotationsDao
|
||||
import llc.arma.ble.data.repository.extensions.checkPermission
|
||||
import llc.arma.ble.data.repository.extensions.fromByte
|
||||
import llc.arma.ble.data.repository.extensions.get4byteUIntAt
|
||||
import llc.arma.ble.data.repository.extensions.info
|
||||
import llc.arma.ble.data.repository.extensions.sendData
|
||||
import llc.arma.ble.data.repository.extensions.toTemperature
|
||||
import llc.arma.ble.domain.Result
|
||||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
|
|
@ -67,7 +68,8 @@ val flashWriteUUID: UUID = UUID.fromString("a77db6f2-9bc4-11ed-a8fc-0242ac120002
|
|||
|
||||
@Singleton
|
||||
class BleRepositoryImpl @Inject constructor(
|
||||
private val app: Application
|
||||
private val app: Application,
|
||||
private val rotationsDao: RotationsDao
|
||||
) : BleRepository {
|
||||
|
||||
val resultList: MutableMap<String, BleInfo> = Collections.synchronizedMap(mutableMapOf<String, BleInfo>())
|
||||
|
|
@ -536,13 +538,17 @@ class BleRepositoryImpl @Inject constructor(
|
|||
|
||||
request.tx?.let {
|
||||
|
||||
connection.discoverServices().findService(serviceUUID)?.findCharacteristic(txWriteUUID)?.write(
|
||||
connection.discoverServices().findService(serviceUUID)?.findCharacteristic(
|
||||
txWriteUUID
|
||||
)?.write(
|
||||
DataByteArray.from(it.sendData)
|
||||
) ?: return Result.failure(BleException.UnexpectedResponse)
|
||||
|
||||
}
|
||||
|
||||
connection.discoverServices().findService(serviceUUID)?.findCharacteristic(flashWriteUUID)!!.write(
|
||||
connection.discoverServices().findService(serviceUUID)?.findCharacteristic(
|
||||
flashWriteUUID
|
||||
)!!.write(
|
||||
DataByteArray.from(9)
|
||||
)
|
||||
|
||||
|
|
@ -579,6 +585,8 @@ class BleRepositoryImpl @Inject constructor(
|
|||
(this shr (it * Byte.SIZE_BITS)).toByte()
|
||||
}.toByteArray()
|
||||
|
||||
rotationsDao.deleteBySerial(serial)
|
||||
|
||||
return if(app.checkPermission()) {
|
||||
|
||||
val connection =
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package llc.arma.ble.data
|
||||
package llc.arma.ble.data.repository
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package llc.arma.ble.data
|
||||
package llc.arma.ble.data.repository
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
|
|
@ -6,11 +6,11 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import llc.arma.ble.data.extensions.checkPermission
|
||||
import llc.arma.ble.data.extensions.fromByte
|
||||
import llc.arma.ble.data.extensions.get2byteShortAt
|
||||
import llc.arma.ble.data.extensions.get2byteUIntAt
|
||||
import llc.arma.ble.data.extensions.get4byteUIntAt
|
||||
import llc.arma.ble.data.repository.extensions.checkPermission
|
||||
import llc.arma.ble.data.repository.extensions.fromByte
|
||||
import llc.arma.ble.data.repository.extensions.get2byteShortAt
|
||||
import llc.arma.ble.data.repository.extensions.get2byteUIntAt
|
||||
import llc.arma.ble.data.repository.extensions.get4byteUIntAt
|
||||
import llc.arma.ble.domain.Result
|
||||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
package llc.arma.ble.data
|
||||
package llc.arma.ble.data.repository
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import llc.arma.ble.data.extensions.checkPermission
|
||||
import llc.arma.ble.data.extensions.get2byteShortAt
|
||||
import llc.arma.ble.data.extensions.sendData
|
||||
import llc.arma.ble.data.repository.extensions.checkPermission
|
||||
import llc.arma.ble.data.repository.extensions.get2byteShortAt
|
||||
import llc.arma.ble.data.repository.extensions.sendData
|
||||
import llc.arma.ble.domain.Result
|
||||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.model.Ble
|
||||
|
|
@ -1,21 +1,16 @@
|
|||
package llc.arma.ble.data
|
||||
package llc.arma.ble.data.repository
|
||||
|
||||
import android.app.Application
|
||||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCallback
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattDescriptor
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import llc.arma.ble.data.extensions.checkPermission
|
||||
import llc.arma.ble.data.extensions.get2byteShortAt
|
||||
import llc.arma.ble.data.extensions.get2byteUIntAt
|
||||
import llc.arma.ble.data.extensions.get4byteUIntAt
|
||||
import llc.arma.ble.data.extensions.sendData
|
||||
import llc.arma.ble.data.repository.extensions.checkPermission
|
||||
import llc.arma.ble.data.repository.extensions.get2byteShortAt
|
||||
import llc.arma.ble.data.repository.extensions.get2byteUIntAt
|
||||
import llc.arma.ble.data.repository.extensions.get4byteUIntAt
|
||||
import llc.arma.ble.data.repository.extensions.sendData
|
||||
import llc.arma.ble.domain.Result
|
||||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
|
|
@ -27,8 +22,6 @@ 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 java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder.LITTLE_ENDIAN
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
|
|
@ -1,19 +1,14 @@
|
|||
package llc.arma.ble.data
|
||||
package llc.arma.ble.data.repository
|
||||
|
||||
import android.app.Application
|
||||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCallback
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import llc.arma.ble.data.extensions.checkPermission
|
||||
import llc.arma.ble.data.extensions.get2byteUIntAt
|
||||
import llc.arma.ble.data.extensions.get4byteUIntAt
|
||||
import llc.arma.ble.data.extensions.toTemperature
|
||||
import llc.arma.ble.data.repository.extensions.checkPermission
|
||||
import llc.arma.ble.data.repository.extensions.get2byteUIntAt
|
||||
import llc.arma.ble.data.repository.extensions.get4byteUIntAt
|
||||
import llc.arma.ble.data.repository.extensions.toTemperature
|
||||
import llc.arma.ble.domain.Result
|
||||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package llc.arma.ble.data.repository
|
||||
|
||||
import llc.arma.ble.data.db.RotationsDao
|
||||
import llc.arma.ble.data.model.RotationEntity
|
||||
import llc.arma.ble.data.model.WheelEntity
|
||||
import llc.arma.ble.domain.model.Rotation
|
||||
import llc.arma.ble.domain.repository.RotationsRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class RotationsRepositoryImpl @Inject constructor(
|
||||
private val dao: RotationsDao
|
||||
) : RotationsRepository {
|
||||
|
||||
override suspend fun saveRotations(rotations: List<Rotation>) {
|
||||
dao.deleteBySerialAndDateMoreThan(rotations.first().bleId, rotations.minOf { it.date } - 8)
|
||||
dao.save(
|
||||
rotations.map {
|
||||
RotationEntity(
|
||||
bleId = it.bleId,
|
||||
rotations = it.rotations,
|
||||
date = it.date
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getRotationsBySerial(serial: String): List<Rotation> {
|
||||
return dao.getBySerialSortedByDate(serial).map {
|
||||
Rotation(
|
||||
bleId = it.bleId,
|
||||
rotations = it.rotations,
|
||||
date = it.date
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteRotationsBySerial(serial: String) {
|
||||
dao.deleteBySerial(serial)
|
||||
}
|
||||
|
||||
override suspend fun getWheelRadiusBySerial(serial: String): Long? {
|
||||
return dao.getWheelBySerial(serial)?.radius
|
||||
}
|
||||
|
||||
override suspend fun setWheelRadius(serial: String, radius: Long) {
|
||||
dao.save(WheelEntity(serial, radius))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
package llc.arma.ble.data
|
||||
package llc.arma.ble.data.repository
|
||||
|
||||
import android.app.Application
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import llc.arma.ble.R
|
||||
import llc.arma.ble.domain.model.Ble
|
||||
import llc.arma.ble.domain.repository.XlsxRepository
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package llc.arma.ble.data.extensions
|
||||
package llc.arma.ble.data.repository.extensions
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Application
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package llc.arma.ble.data.extensions
|
||||
package llc.arma.ble.data.repository.extensions
|
||||
|
||||
import llc.arma.ble.domain.model.Ble
|
||||
import llc.arma.ble.domain.usecase.AccelScale
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package llc.arma.ble.data.extensions
|
||||
package llc.arma.ble.data.repository.extensions
|
||||
|
||||
import llc.arma.ble.domain.model.BleInfo
|
||||
import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResult
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package llc.arma.ble.data.extensions
|
||||
package llc.arma.ble.data.repository.extensions
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package llc.arma.ble.domain.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
|
||||
data class Rotation(
|
||||
|
||||
val bleId: String,
|
||||
|
||||
val date: Long,
|
||||
|
||||
val rotations: Long
|
||||
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package llc.arma.ble.domain.repository
|
||||
|
||||
import llc.arma.ble.domain.model.Rotation
|
||||
|
||||
interface RotationsRepository {
|
||||
|
||||
suspend fun saveRotations(rotations: List<Rotation>)
|
||||
|
||||
suspend fun getRotationsBySerial(serial: String): List<Rotation>
|
||||
|
||||
suspend fun deleteRotationsBySerial(serial: String)
|
||||
|
||||
suspend fun getWheelRadiusBySerial(serial: String): Long?
|
||||
|
||||
suspend fun setWheelRadius(serial: String, radius: Long)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package llc.arma.ble.domain.usecase
|
||||
|
||||
import llc.arma.ble.domain.model.Rotation
|
||||
import llc.arma.ble.domain.repository.RotationsRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class DeleteRotationsBySerial @Inject constructor (
|
||||
private val rotationsRepository: RotationsRepository
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(serial: String) {
|
||||
|
||||
rotationsRepository.deleteRotationsBySerial(serial)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,20 +1,49 @@
|
|||
package llc.arma.ble.domain.usecase
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
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.Rotation
|
||||
import llc.arma.ble.domain.repository.BleRepository
|
||||
import llc.arma.ble.domain.repository.RotationsRepository
|
||||
import java.time.LocalDateTime
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetAccelerometerHistoryBySerial @Inject constructor(
|
||||
private val bleRepository: BleRepository
|
||||
private val bleRepository: BleRepository,
|
||||
private val rotationsRepository: RotationsRepository
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(serial: String): Flow<Result<ProgressState<List<Ble.Accelerometer.HistoryPoint>>, BleException>> {
|
||||
|
||||
return bleRepository.getAccelerometerHistoryBySerial(serial)
|
||||
return bleRepository.getAccelerometerHistoryBySerial(serial).onEach {
|
||||
it.onSuccess { progress ->
|
||||
if(progress is ProgressState.Finished){
|
||||
val rotations = progress.data.mapNotNull { point ->
|
||||
if(point is Ble.Accelerometer.HistoryPoint.Rotation){
|
||||
Rotation(
|
||||
bleId = serial,
|
||||
date = TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(point.date)),
|
||||
rotations = point.value
|
||||
)
|
||||
}else{
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
if(rotations.isNotEmpty()){
|
||||
|
||||
rotationsRepository.saveRotations(rotations)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package llc.arma.ble.domain.usecase
|
||||
|
||||
import llc.arma.ble.domain.model.Rotation
|
||||
import llc.arma.ble.domain.repository.RotationsRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetRotationsBySerial @Inject constructor (
|
||||
private val rotationsRepository: RotationsRepository
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(serial: String): List<Rotation> {
|
||||
|
||||
return rotationsRepository.getRotationsBySerial(serial)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package llc.arma.ble.domain.usecase
|
||||
|
||||
import llc.arma.ble.domain.repository.RotationsRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetWheelRadiusBySerial @Inject constructor(
|
||||
private val rotationsRepository: RotationsRepository
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(serial: String): Long? {
|
||||
return rotationsRepository.getWheelRadiusBySerial(serial)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package llc.arma.ble.domain.usecase
|
||||
|
||||
import llc.arma.ble.domain.repository.RotationsRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class SetWheelRadius @Inject constructor(
|
||||
private val rotationsRepository: RotationsRepository
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(serial: String, radius: Long){
|
||||
rotationsRepository.setWheelRadius(serial, radius)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue