Add accelerometer

This commit is contained in:
Vineyro 2023-06-05 17:00:20 +07:00
parent e8853d8cda
commit 1270f48422
46 changed files with 844 additions and 101 deletions

View File

@ -7,6 +7,7 @@
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

View File

@ -1,6 +1,6 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="temurin-11" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -29,11 +29,11 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}
buildFeatures {
compose true
@ -50,23 +50,23 @@ android {
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
implementation 'androidx.activity:activity-compose:1.7.0'
implementation "androidx.compose.ui:ui:1.5.0-alpha01"
implementation "androidx.compose.ui:ui-tooling-preview:1.5.0-alpha01"
implementation 'androidx.compose.material3:material3:1.1.0-beta01'
implementation 'androidx.compose.material:material:1.5.0-alpha01'
implementation 'androidx.activity:activity-compose:1.7.2'
implementation "androidx.compose.ui:ui:1.5.0-beta01"
implementation "androidx.compose.ui:ui-tooling-preview:1.5.0-beta01"
implementation 'androidx.compose.material3:material3:1.2.0-alpha02'
implementation 'androidx.compose.material:material:1.5.0-beta01'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.5.0-alpha01"
debugImplementation "androidx.compose.ui:ui-tooling:1.5.0-alpha01"
debugImplementation "androidx.compose.ui:ui-test-manifest:1.5.0-alpha01"
androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.5.0-beta01"
debugImplementation "androidx.compose.ui:ui-tooling:1.5.0-beta01"
debugImplementation "androidx.compose.ui:ui-test-manifest:1.5.0-beta01"
implementation "androidx.compose.material:material-icons-extended:1.5.0-alpha01"
implementation "androidx.compose.material:material-icons-extended:1.5.0-beta01"
implementation 'androidx.core:core-splashscreen:1.0.0'
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation 'androidx.navigation:navigation-compose:2.5.3'
implementation("androidx.hilt:hilt-navigation-compose:1.1.0-alpha01")
@ -76,8 +76,8 @@ dependencies {
implementation "com.google.accompanist:accompanist-permissions:0.26.3-beta"
implementation "com.patrykandpatrick.vico:core:1.6.4"
implementation "com.patrykandpatrick.vico:compose:1.6.4"
implementation "com.patrykandpatrick.vico:compose-m3:1.6.4"
implementation "com.patrykandpatrick.vico:core:1.6.6"
implementation "com.patrykandpatrick.vico:compose:1.6.6"
implementation "com.patrykandpatrick.vico:compose-m3:1.6.6"
}

View File

@ -29,7 +29,7 @@ class BottomState @OptIn(ExperimentalMaterialApi::class) constructor(
)
class BottomDialogState @OptIn(ExperimentalMaterialApi::class) constructor(
private val sheetState: ModalBottomSheetState?,
val sheetState: ModalBottomSheetState?,
val setContent: (@Composable () -> Unit) -> Unit,
) {

View File

@ -31,6 +31,12 @@ class BleMapper @Inject constructor(
)
)
}
is Ble.Accelerometer -> {
BleView.Accelerometer(
info = input.info,
)
}
}
}

View File

@ -31,6 +31,12 @@ class BleViewMapper @Inject constructor(
)
)
}
is BleView.Accelerometer -> {
Ble.Accelerometer(
info = input.info
)
}
}
}

View File

@ -9,6 +9,10 @@ sealed class BleView(
val info: BleInfo
) {
class Accelerometer(
info: BleInfo
) : BleView(info)
class Beacon(
info: BleInfo,
val state: BleState

View File

@ -35,6 +35,7 @@ fun BleInfoView(
imageVector = when(bleInfo.type){
BleInfo.Type.BEACON -> Icons.Rounded.Nfc
BleInfo.Type.THERMOMETER -> Icons.Rounded.Thermostat
BleInfo.Type.ACCELEROMETER -> Icons.Rounded.Speed
},
contentDescription = null
)
@ -43,6 +44,7 @@ fun BleInfoView(
subtitle = when(bleInfo.type){
BleInfo.Type.BEACON -> "Маяк"
BleInfo.Type.THERMOMETER -> "Термодатчик"
BleInfo.Type.ACCELEROMETER -> "Акселерометр"
}
)

View File

@ -240,6 +240,7 @@ private fun BleItem(
imageVector = when(ble.type){
BleInfo.Type.BEACON -> Icons.Rounded.Nfc
BleInfo.Type.THERMOMETER -> Icons.Rounded.Thermostat
BleInfo.Type.ACCELEROMETER -> Icons.Rounded.Speed
},
contentDescription = null
)

View File

@ -38,7 +38,8 @@ class BleListViewModel @Inject constructor(
}
override fun setInitialState(): BleListContract.State = BleListContract.State(emptyList(), emptyList(), BleListContract.State.Filter())
override fun setInitialState(): BleListContract.State =
BleListContract.State(emptyList(), emptyList(), BleListContract.State.Filter())
override fun handleEvents(event: BleListContract.Event) {
when(event){

View File

@ -43,6 +43,7 @@ private val BleInfo.Type?.localized: String
return when(this){
BleInfo.Type.BEACON -> "Маяк"
BleInfo.Type.THERMOMETER -> "Термодатчик"
BleInfo.Type.ACCELEROMETER -> "Акселерометр"
null -> "Все"
}
}

View File

@ -4,8 +4,8 @@ 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.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.GetBleBySerial

View File

@ -20,10 +20,11 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.BleInfoView
import llc.arma.ble.app.ui.screen.beacon.BeaconScreen
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerScreen
import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconScreen
import llc.arma.ble.app.ui.screen.password.ChangePasswordContract
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.thermometer.ThermometerScreen
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerScreen
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.GetBleBySerial
@ -114,6 +115,10 @@ fun ConnectionScreen(
}
}
is Ble.Accelerometer -> {
AccelerometerScreen(ble = state.ble)
}
}
}

View File

@ -10,8 +10,8 @@ import llc.arma.ble.app.ui.common.BaseViewModel
import llc.arma.ble.app.ui.mapper.BleMapper
import llc.arma.ble.app.ui.mapper.BleViewMapper
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.GetBleBySerial
import llc.arma.ble.domain.usecase.WriteBle

View File

@ -0,0 +1,41 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer
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.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
class AccelerometerContract {
sealed class Event : ViewEvent {
object OnShowAccelerometerMeasure : Event()
object OnHideAccelerometerMeasure : Event()
data class OnBleChanged(
val ble: Ble.Accelerometer,
): Event()
}
sealed class State : ViewState {
object Loading : State()
data class Display(
val origin: Ble.Accelerometer,
val accelerometer: BleView.Accelerometer,
) : State()
}
sealed class Effect : ViewSideEffect {
object ShowAccelerometerMeasure : Effect()
}
}

View File

@ -0,0 +1,92 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer
import androidx.compose.foundation.layout.Column
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.hilt.navigation.compose.hiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.rememberBottomDialogState
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelerometerMeasure
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.DisplayState
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.LoadingState
import llc.arma.ble.domain.model.Ble
enum class SheetPage {
MEASURE_HISTORY
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun AccelerometerScreen(
ble: Ble.Accelerometer,
) {
val viewModel = hiltViewModel<AccelerometerViewModel>()
val state = viewModel.viewState.value
val bottomDialog = rememberBottomDialogState()
LaunchedEffect(ble){
viewModel.setEvent(AccelerometerContract.Event.OnBleChanged(ble))
}
var sheetPage by rememberSaveable {
mutableStateOf<SheetPage?>(null)
}
LaunchedEffect(sheetPage) {
when (sheetPage) {
SheetPage.MEASURE_HISTORY -> launch {
val currentState = viewModel.viewState.value
if (currentState is AccelerometerContract.State.Display) {
bottomDialog.show {
AccelerometerMeasure(ble = currentState.accelerometer.info)
}
}
}
null -> {
bottomDialog.hide()
}
}
}
LaunchedEffect("effect"){
viewModel.effect.onEach {
when(it){
AccelerometerContract.Effect.ShowAccelerometerMeasure -> {
sheetPage = null
delay(100)
sheetPage = SheetPage.MEASURE_HISTORY
}
}
}.launchIn(this)
}
Column {
when(state){
is AccelerometerContract.State.Display -> {
DisplayState(
origin = state.origin,
ble = state.accelerometer,
onEvent = {
viewModel.setEvent(it)
}
)
}
is AccelerometerContract.State.Loading -> LoadingState()
}
}
}

View File

@ -0,0 +1,70 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer
import dagger.hilt.android.lifecycle.HiltViewModel
import llc.arma.ble.app.ui.common.BaseViewModel
import llc.arma.ble.app.ui.mapper.BleMapper
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
import javax.inject.Inject
@HiltViewModel
class AccelerometerViewModel @Inject constructor(
private val bleMapper: BleMapper
) : BaseViewModel<AccelerometerContract.State, AccelerometerContract.Event, AccelerometerContract.Effect>() {
override fun setInitialState() = AccelerometerContract.State.Loading
override fun handleEvents(event: AccelerometerContract.Event) {
when(event){
is AccelerometerContract.Event.OnBleChanged -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnHideAccelerometerMeasure -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnShowAccelerometerMeasure -> reduce(viewState.value, event)
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnHideAccelerometerMeasure
) {
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnShowAccelerometerMeasure
) {
setEffect {
AccelerometerContract.Effect.ShowAccelerometerMeasure
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnBleChanged
) {
when (state) {
is AccelerometerContract.State.Display -> setState {
state.copy(
origin = Ble.Accelerometer(
info = event.ble.info
)
)
}
is AccelerometerContract.State.Loading -> setState {
AccelerometerContract.State.Display(
origin = event.ble,
accelerometer = bleMapper.map(event.ble) as BleView.Accelerometer
)
}
}
}
}

View File

@ -0,0 +1,301 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
import com.patrykandpatrick.vico.compose.axis.vertical.startAxis
import com.patrykandpatrick.vico.compose.chart.Chart
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.BaseViewModel
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.BleInfo
import javax.inject.Inject
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.ui.text.style.TextAlign
import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis
import com.patrykandpatrick.vico.compose.chart.column.columnChart
import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec
import com.patrykandpatrick.vico.core.chart.scale.AutoScaleUp
import com.patrykandpatrick.vico.core.entry.FloatEntry
import com.patrykandpatrick.vico.core.scroll.AutoScrollCondition
import com.patrykandpatrick.vico.core.scroll.InitialScroll
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import llc.arma.ble.domain.usecase.GetAccelerometerMeasureBySerialFlow
@Composable
fun AccelerometerMeasure(
ble: BleInfo
) {
val viewModel = hiltViewModel<AccelerometerHistoryViewModel>()
val state = viewModel.viewState.value
LaunchedEffect(ble.serial) {
viewModel.setEvent(AccelerometerMeasureContract.Event.OnStart(ble.serial))
}
DisposableEffect(key1 = ble.serial, effect = {
onDispose {
viewModel.setEvent(AccelerometerMeasureContract.Event.StopMeasure)
}
})
Column(
modifier = Modifier.fillMaxHeight(0.9f)
) {
Row(
modifier = Modifier.padding(horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
val title = when(state){
is AccelerometerMeasureContract.State.Display -> {
"История измерений"
}
AccelerometerMeasureContract.State.Exception -> "История измерений"
}
Text(
modifier = Modifier.weight(1f),
text = title,
style = MaterialTheme.typography.titleLarge
)
IconButton(
onClick = {
viewModel.setEvent(AccelerometerMeasureContract.Event.OnRefreshHistory(ble.serial))
},
enabled = true
) {
Icon(
imageVector = Icons.Rounded.Refresh,
contentDescription = null
)
}
}
Spacer(modifier = Modifier.height(16.dp))
Box(modifier = Modifier) {
when (state) {
is AccelerometerMeasureContract.State.Display -> Display(state = state)
AccelerometerMeasureContract.State.Exception -> Exception()
}
}
}
}
@Composable
fun Display(
state: AccelerometerMeasureContract.State.Display
) {
Box(modifier = Modifier
.padding(8.dp)
.fillMaxSize()
) {
if (state.measureHistory.isEmpty()) {
Text(
modifier = Modifier.align(Alignment.Center),
text = "Нет данных"
)
} else {
val producer = remember {
ChartEntryModelProducer(listOf<FloatEntry>())
}
producer.setEntries(state.measureHistory.mapIndexed { index, measurePoint ->
FloatEntry(index.toFloat(), measurePoint)
})
val lineChart = columnChart()
Chart(
chart = lineChart,
chartModelProducer = producer,
startAxis = startAxis(),
bottomAxis = bottomAxis(),
modifier = Modifier.fillMaxSize(),
autoScaleUp = AutoScaleUp.None,
diffAnimationSpec = tween(0),
chartScrollSpec = rememberChartScrollSpec(
initialScroll = InitialScroll.End,
autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased
)
)
}
}
}
@Composable
fun Exception(
) {
Box(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
.aspectRatio(2f),
){
Text(
textAlign = TextAlign.Center,
text = "Во время загрузки произошла ошибка",
modifier = Modifier.align(Alignment.Center)
)
}
}
class AccelerometerMeasureContract {
sealed class Event : ViewEvent {
object StopMeasure : Event()
data class OnStart(
val serial: String
) : Event()
data class OnRefreshHistory(
val serial: String
) : Event()
}
sealed class State : ViewState {
data class Display(
val measureHistory : List<Float>
) : State()
object Exception : State()
}
sealed class Effect : ViewSideEffect {
}
}
@HiltViewModel
class AccelerometerHistoryViewModel @Inject constructor(
private val getAccelerometerMeasureBySerialFlow: GetAccelerometerMeasureBySerialFlow,
) : BaseViewModel<AccelerometerMeasureContract.State, AccelerometerMeasureContract.Event, AccelerometerMeasureContract.Effect>() {
var measureJob: Job? = null
private var lastSerial: String? = null
override fun setInitialState() = AccelerometerMeasureContract.State.Display(
emptyList()
)
override fun handleEvents(event: AccelerometerMeasureContract.Event) {
when(event){
is AccelerometerMeasureContract.Event.OnStart -> reduce(viewState.value, event)
is AccelerometerMeasureContract.Event.OnRefreshHistory -> reduce(viewState.value, event)
is AccelerometerMeasureContract.Event.StopMeasure -> reduce(viewState.value, event)
}
}
private fun reduce(
state: AccelerometerMeasureContract.State,
event: AccelerometerMeasureContract.Event.StopMeasure
) {
measureJob?.cancel()
setState {
AccelerometerMeasureContract.State.Display(emptyList())
}
}
private fun reduce(
state: AccelerometerMeasureContract.State,
event: AccelerometerMeasureContract.Event.OnStart
) {
startReadMeasure(event.serial, false)
}
private fun reduce(
state: AccelerometerMeasureContract.State,
event: AccelerometerMeasureContract.Event.OnRefreshHistory
) {
startReadMeasure(event.serial, true)
}
private fun startReadMeasure(serial: String, restartJob: Boolean){
if(restartJob || measureJob == null) {
measureJob = viewModelScope.launch {
setState {
AccelerometerMeasureContract.State.Display(emptyList())
}
getAccelerometerMeasureBySerialFlow(serial).onEach {
it.fold(
onSuccess = {
setState {
when (this) {
is AccelerometerMeasureContract.State.Display -> {
val dataList = this.measureHistory.toMutableList().apply {
add(it)
}
AccelerometerMeasureContract.State.Display(dataList)
}
AccelerometerMeasureContract.State.Exception -> {
AccelerometerMeasureContract.State.Display(listOf(it))
}
}
}
},
onFailure = {
setState {
AccelerometerMeasureContract.State.Exception
}
}
)
}.launchIn(this)
}
}
}
}

View File

@ -0,0 +1,121 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material.icons.rounded.KeyboardArrowRight
import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.BleInfoView
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
@Composable
fun DisplayState(
onEvent: (AccelerometerContract.Event) -> Unit,
origin: Ble.Accelerometer,
ble: BleView.Accelerometer
) {
Column() {
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.weight(1f)
) {
Box(
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 8.dp
)
) {
BleInfoView(bleInfo = origin.info)
}
Column(
modifier = Modifier,
content = {
Box(
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 8.dp
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable {
onEvent(AccelerometerContract.Event.OnShowAccelerometerMeasure)
}
.padding(8.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "График измерений"
)
}
Icon(
imageVector = Icons.Rounded.KeyboardArrowRight,
contentDescription = null
)
}
}
}
)
}
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
onClick = {
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.background,
style = MaterialTheme.typography.labelLarge,
text = "Сохранить"
)
}
}
}
}

View File

@ -0,0 +1,19 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@Composable
fun LoadingState(){
Box(modifier = Modifier.fillMaxSize()) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}

View File

@ -1,10 +1,10 @@
package llc.arma.ble.app.ui.screen.beacon
package llc.arma.ble.app.ui.screen.inspection.beacon
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.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
class BeaconContract {

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.beacon
package llc.arma.ble.app.ui.screen.inspection.beacon
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
@ -19,10 +19,10 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.rememberBottomDialogState
import llc.arma.ble.app.ui.screen.beacon.view.DisplayState
import llc.arma.ble.app.ui.screen.beacon.view.PowerEdit
import llc.arma.ble.app.ui.screen.beacon.view.Write
import llc.arma.ble.app.ui.screen.thermometer.localizedName
import llc.arma.ble.app.ui.screen.inspection.beacon.view.DisplayState
import llc.arma.ble.app.ui.screen.inspection.beacon.view.PowerEdit
import llc.arma.ble.app.ui.screen.inspection.beacon.view.Write
import llc.arma.ble.app.ui.screen.inspection.thermometer.localizedName
import llc.arma.ble.domain.model.Ble
enum class SheetPage {

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.beacon
package llc.arma.ble.app.ui.screen.inspection.beacon
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
@ -11,7 +11,7 @@ import llc.arma.ble.app.ui.common.BaseViewModel
import llc.arma.ble.app.ui.mapper.BleMapper
import llc.arma.ble.app.ui.mapper.BleViewMapper
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.usecase.WriteBle

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.beacon.view
package llc.arma.ble.app.ui.screen.inspection.beacon.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@ -17,7 +17,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.BleInfoView
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract
import llc.arma.ble.domain.model.Ble
@Composable

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.beacon.view
package llc.arma.ble.app.ui.screen.inspection.beacon.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@ -14,8 +14,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
@Composable
fun PowerEdit(

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.beacon.view
package llc.arma.ble.app.ui.screen.inspection.beacon.view
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
@ -17,8 +17,8 @@ import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import llc.arma.ble.R
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.thermometer.localizedName
import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.localizedName
@Composable
fun Write(

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.thermometer
package llc.arma.ble.app.ui.screen.inspection.thermometer
import llc.arma.ble.app.ui.common.ViewEvent
import llc.arma.ble.app.ui.common.ViewSideEffect

View File

@ -1,34 +1,26 @@
package llc.arma.ble.app.ui.screen.thermometer
package llc.arma.ble.app.ui.screen.inspection.thermometer
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.layout.Column
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.runtime.setValue
import androidx.hilt.navigation.compose.hiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import llc.arma.ble.R
import llc.arma.ble.app.ui.common.rememberBottomDialogState
import llc.arma.ble.app.ui.screen.thermometer.view.*
import llc.arma.ble.app.ui.screen.inspection.thermometer.view.DisplayState
import llc.arma.ble.app.ui.screen.inspection.thermometer.view.IntervalEdit
import llc.arma.ble.app.ui.screen.inspection.thermometer.view.LoadingState
import llc.arma.ble.app.ui.screen.inspection.thermometer.view.PowerEdit
import llc.arma.ble.app.ui.screen.inspection.thermometer.view.TemperatureHistory
import llc.arma.ble.app.ui.screen.inspection.thermometer.view.Write
import llc.arma.ble.domain.model.Ble
enum class SheetPage {
@ -74,7 +66,6 @@ fun ThermometerScreen(
val viewModel = hiltViewModel<ThermometerViewModel>()
val state = viewModel.viewState.value
val bottomDialog = rememberBottomDialogState()
LaunchedEffect(sheetPage){
@ -142,8 +133,6 @@ fun ThermometerScreen(
}
}
val writeSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
LaunchedEffect("effect"){
viewModel.effect.onEach {
when(it){

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.thermometer
package llc.arma.ble.app.ui.screen.inspection.thermometer
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
@ -201,7 +201,9 @@ class ThermometerViewModel @Inject constructor(
setState {
state.copy(
writeState = ThermometerContract.State.Display.WriteState.Writing(request.writeRequest)
writeState = ThermometerContract.State.Display.WriteState.Writing(
request.writeRequest
)
)
}

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.thermometer.view
package llc.arma.ble.app.ui.screen.inspection.thermometer.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@ -18,7 +18,8 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.BleInfoView
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
@Composable

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.thermometer.view
package llc.arma.ble.app.ui.screen.inspection.thermometer.view
import androidx.compose.animation.*
import androidx.compose.foundation.layout.*
@ -12,7 +12,7 @@ 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.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
@Composable
fun IntervalEdit(
@ -165,11 +165,11 @@ fun NumberPicker(
targetState = value,
transitionSpec = {
if (targetState > initialState) {
slideInVertically { height -> height } + fadeIn() with
slideOutVertically { height -> -height } + fadeOut()
(slideInVertically { height -> height } + fadeIn()).togetherWith(
slideOutVertically { height -> -height } + fadeOut())
} else {
slideInVertically { height -> -height } + fadeIn() with
slideOutVertically { height -> height } + fadeOut()
(slideInVertically { height -> -height } + fadeIn()).togetherWith(
slideOutVertically { height -> height } + fadeOut())
}.using(
SizeTransform(clip = false)
)

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.thermometer.view
package llc.arma.ble.app.ui.screen.inspection.thermometer.view
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.thermometer.view
package llc.arma.ble.app.ui.screen.inspection.thermometer.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@ -14,7 +14,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
@Composable
fun PowerEdit(

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.thermometer.view
package llc.arma.ble.app.ui.screen.inspection.thermometer.view
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState

View File

@ -1,4 +1,4 @@
package llc.arma.ble.app.ui.screen.thermometer.view
package llc.arma.ble.app.ui.screen.inspection.thermometer.view
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
@ -19,8 +19,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import llc.arma.ble.R
import llc.arma.ble.app.ui.screen.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.thermometer.localizedName
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.localizedName
@Composable
fun Write(

View File

@ -6,9 +6,9 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.dialog
import androidx.navigation.compose.rememberNavController
import llc.arma.ble.app.ui.screen.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.thermometer.ThermometerScreen
import llc.arma.ble.app.ui.screen.beacon.BeaconScreen
import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerScreen
import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconScreen
import llc.arma.ble.app.ui.screen.ble.BleListContract
import llc.arma.ble.app.ui.screen.ble.BleListScreen
import llc.arma.ble.app.ui.screen.connection.ConnectionContract

View File

@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import llc.arma.ble.app.ui.screen.password.view.Loading
import llc.arma.ble.app.ui.screen.password.view.Result
import llc.arma.ble.app.ui.screen.thermometer.view.LoadingState
import llc.arma.ble.app.ui.screen.inspection.thermometer.view.LoadingState
@Composable
fun ChangePasswordScreen(

View File

@ -72,7 +72,7 @@ fun BleTheme(
SideEffect {
currentWindow.statusBarColor = LightColorScheme.primary.copy(alpha = 0f).toArgb()
WindowCompat.getInsetsController(currentWindow, view).isAppearanceLightStatusBars = darkTheme.not()
WindowCompat.getInsetsController(currentWindow, view).isAppearanceLightStatusBars = true
}
}

View File

@ -11,6 +11,7 @@ import android.os.Build
import android.os.SystemClock
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.util.toRange
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@ -27,6 +28,7 @@ import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.resume
import kotlin.random.Random
val serviceUUID: UUID = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002")
@ -83,8 +85,9 @@ class BleRepositoryImpl @Inject constructor(
private val ScanResult.type: BleInfo.Type
get() {
return when(scanRecord?.manufacturerSpecificData?.get(89)?.get(0)?.toUByte()?.toInt()){
1 -> BleInfo.Type.BEACON
2 -> BleInfo.Type.THERMOMETER
else -> BleInfo.Type.BEACON
else -> BleInfo.Type.ACCELEROMETER
}
}
@ -135,13 +138,13 @@ class BleRepositoryImpl @Inject constructor(
if (checkPermission()) {
if (result.scanRecord?.deviceName?.contains("ArmA") == true) {
//if (result.scanRecord?.deviceName?.contains("ArmA") == true) {
resultList[result.device.address] = result.info
deviceCache[result.device.address] = result
}
//}
} else {
CoroutineScope(Dispatchers.IO).launch {
@ -227,6 +230,38 @@ class BleRepositoryImpl @Inject constructor(
deviceCache[serial]?.let { result ->
return when(result.info.type) {
BleInfo.Type.ACCELEROMETER -> {
Result.success(
flow {
while (true) {
deviceCache[serial]?.let { newResult ->
emit(
Ble.Accelerometer(
info = newResult.info.copy(
rssi = if((SystemClock.elapsedRealtimeNanos() - newResult.timestampNanos) > 15_000_000_000) {
null
} else {
newResult.rssi
}
)
)
)
}
delay(1_000)
}
}
)
}
BleInfo.Type.BEACON -> {
Result.success(
flow {
@ -342,6 +377,9 @@ class BleRepositoryImpl @Inject constructor(
}
BleInfo.Type.ACCELEROMETER -> {
TODO()
}
}
}
@ -577,6 +615,19 @@ class BleRepositoryImpl @Inject constructor(
return Result.success(Unit)
}
override fun getAccelerometerMeasureBySerialFlow(serial: String): Flow<Result<Float, BleException>> {
return flow {
while (true){
delay(1000)
emit(
Result.success(((-256)..256).random().toFloat())
)
}
}
}
private suspend fun readCharacteristic(
device: BluetoothDevice,
serviceId: UUID,

View File

@ -4,6 +4,12 @@ sealed class Ble(
val info: BleInfo
) {
class Accelerometer(
info: BleInfo
): Ble(info){
}
class Beacon(
info: BleInfo,
val state: BleState

View File

@ -10,7 +10,7 @@ data class BleInfo(
){
enum class Type {
BEACON, THERMOMETER
BEACON, THERMOMETER, ACCELEROMETER
}
}

View File

@ -24,5 +24,6 @@ interface BleRepository {
suspend fun writeBle(serial: String, request: Ble.Beacon.WriteRequest): Result<Unit, BleException>
suspend fun changeBlePassword(password: String, serial: String): Result<Unit, BleException>
fun getAccelerometerMeasureBySerialFlow(serial: String): Flow<Result<Float, BleException>>
}

View File

@ -0,0 +1,21 @@
package llc.arma.ble.domain.usecase
import kotlinx.coroutines.flow.Flow
import llc.arma.ble.domain.Result
import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.repository.BleRepository
import javax.inject.Inject
class GetAccelerometerMeasureBySerialFlow @Inject constructor(
private val bleRepository: BleRepository
) {
operator fun invoke(serial: String): Flow<Result<Float, BleException>> {
return bleRepository.getAccelerometerMeasureBySerialFlow(serial)
}
}

View File

@ -12,7 +12,7 @@
<style name="Theme.Ble" parent="android:Theme.Material.Light.NoActionBar" >
<item name="android:statusBarColor">#00000000</item>
<item name="android:navigationBarColor">#ffffffff</item>
<item name="android:windowLightStatusBar" >true</item>
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>

View File

@ -6,7 +6,7 @@ buildscript {
dependencies {
classpath('com.google.dagger:hilt-android-gradle-plugin:2.45')
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10"
}
repositories {
mavenCentral()
@ -14,7 +14,7 @@ buildscript {
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.0.0' apply false
id 'com.android.library' version '8.0.0' apply false
id 'com.android.application' version '8.0.2' apply false
id 'com.android.library' version '8.0.2' apply false
id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
}

View File

@ -23,3 +23,4 @@ kotlin.code.style=official
android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false
org.gradle.unsafe.configuration-cache=true