Compare commits
No commits in common. "8c1c35afdd3eee6fa94a3e0613dcea1c06f22321" and "2a6bfb205c34fb5e7892019311484168f9af0167" have entirely different histories.
8c1c35afdd
...
2a6bfb205c
14 changed files with 415 additions and 663 deletions
|
@ -1,5 +1,4 @@
|
||||||
@file:OptIn(
|
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class,
|
||||||
ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class,
|
|
||||||
ExperimentalMaterial3Api::class
|
ExperimentalMaterial3Api::class
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -8,20 +7,12 @@ package ru.nm17.narodmon
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Person
|
|
||||||
import androidx.compose.material.icons.rounded.Add
|
|
||||||
import androidx.compose.material.icons.rounded.Menu
|
|
||||||
import androidx.compose.material3.BottomAppBar
|
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FloatingActionButton
|
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -33,9 +24,8 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
@ -46,8 +36,8 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.nm17.narodmon.db.AppDatabase
|
import ru.nm17.narodmon.db.AppDatabase
|
||||||
import ru.nm17.narodmon.db.entities.KVSetting
|
import ru.nm17.narodmon.db.entities.KVSetting
|
||||||
import ru.nm17.narodmon.ui.dialogs.AgreementDialog
|
import ru.nm17.narodmon.ui.elements.AgreementDialog
|
||||||
import ru.nm17.narodmon.ui.sensorsScreen.SensorsScreen
|
import ru.nm17.narodmon.ui.pages.SensorsPage
|
||||||
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,39 +46,18 @@ import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
||||||
fun AppNavHost() {
|
fun AppNavHost() {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val coScope = rememberCoroutineScope()
|
val coScope = rememberCoroutineScope()
|
||||||
|
|
||||||
NavHost(navController = navController, startDestination = "sensors") {
|
NavHost(navController = navController, startDestination = "sensors") {
|
||||||
composable("agreement") {
|
composable("agreement") {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
composable("sensors") {
|
composable("sensors") {
|
||||||
Scaffold(bottomBar = {
|
SensorsPage(navController)
|
||||||
BottomAppBar(actions = {
|
|
||||||
Image(
|
|
||||||
Icons.Rounded.Menu,
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
}, floatingActionButton = {
|
|
||||||
FloatingActionButton(onClick = { /*TODO*/ }) {
|
|
||||||
Image(
|
|
||||||
Icons.Rounded.Add,
|
|
||||||
contentDescription = ""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
contentPadding = PaddingValues(start = 16.dp)
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
Column(modifier = Modifier.padding(it)) {
|
|
||||||
SensorsScreen(navController)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*...*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/*...*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@ -100,20 +69,20 @@ class MainActivity : ComponentActivity() {
|
||||||
AppDatabase::class.java, "data"
|
AppDatabase::class.java, "data"
|
||||||
).build()
|
).build()
|
||||||
|
|
||||||
// val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
||||||
//
|
|
||||||
// val sharedPreferences = EncryptedSharedPreferences.create(
|
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||||
// "secret_shared_prefs",
|
"secret_shared_prefs",
|
||||||
// masterKeyAlias,
|
masterKeyAlias,
|
||||||
// createDeviceProtectedStorageContext(),
|
createDeviceProtectedStorageContext(),
|
||||||
// EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
// EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||||
// )
|
)
|
||||||
|
|
||||||
// use the shared preferences and editor as you normally would
|
// use the shared preferences and editor as you normally would
|
||||||
|
|
||||||
// use the shared preferences and editor as you normally would
|
// use the shared preferences and editor as you normally would
|
||||||
// val credSharedPreferences = sharedPreferences
|
val credSharedPreferences = sharedPreferences
|
||||||
|
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
|
@ -151,9 +120,9 @@ class MainActivity : ComponentActivity() {
|
||||||
Text(text = stringResource(R.string.waiting_for_user_agreement))
|
Text(text = stringResource(R.string.waiting_for_user_agreement))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
AppNavHost()
|
AppNavHost()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A surface container using the 'background' color from the theme
|
// A surface container using the 'background' color from the theme
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package ru.nm17.narodmon.ui
|
|
||||||
|
|
||||||
fun String.toChipTitle(): String {
|
|
||||||
return if (length >= 20) {
|
|
||||||
this.slice(0..16) + ".."
|
|
||||||
} else {
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +1,37 @@
|
||||||
package ru.nm17.narodmon.ui.dialogs
|
package ru.nm17.narodmon.ui.bottomSheets
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.SheetState
|
||||||
|
import androidx.compose.material3.SheetValue
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
import ru.nm17.narodmon.R
|
import ru.nm17.narodmon.R
|
||||||
import ru.nm17.narodmon.ui.elements.FilterCheckbox
|
|
||||||
import ru.nm17.narodmon.ui.entities.SensorFilterUiEntity
|
import ru.nm17.narodmon.ui.entities.SensorFilterUiEntity
|
||||||
|
import ru.nm17.narodmon.ui.pages.FilterCheckbox
|
||||||
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun FilterSensorsDialog(
|
fun FilterSensorsBottomSheet(
|
||||||
onApply: (filters: List<SensorFilterUiEntity>) -> Unit,
|
onApply: (filters: List<SensorFilterUiEntity>) -> Unit,
|
||||||
onDismissRequest: () -> Unit
|
onDismissRequest: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
@ -67,61 +67,53 @@ fun FilterSensorsDialog(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
AlertDialog(
|
onDismissRequest = { onDismissRequest.invoke() },
|
||||||
onDismissRequest = {
|
sheetState = SheetState(true)
|
||||||
onDismissRequest.invoke()
|
) {
|
||||||
}) {
|
Row(
|
||||||
Surface(
|
horizontalArrangement = Arrangement.Center,
|
||||||
shape = MaterialTheme.shapes.large,
|
modifier = Modifier.fillMaxWidth(),
|
||||||
tonalElevation = AlertDialogDefaults.TonalElevation
|
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.padding(vertical = 16.dp)) {
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.sensors_filter_title),
|
text = stringResource(R.string.sensors_filter_title),
|
||||||
style = MaterialTheme.typography.titleLarge,
|
fontSize = 24.sp,
|
||||||
textAlign = TextAlign.Center,
|
fontWeight = FontWeight(500),
|
||||||
modifier = Modifier
|
)
|
||||||
.padding(horizontal = 16.dp)
|
}
|
||||||
.fillMaxWidth()
|
Box(contentAlignment = Alignment.BottomCenter) {
|
||||||
)
|
LazyColumn(
|
||||||
LazyColumn(
|
modifier = Modifier
|
||||||
modifier = Modifier
|
.padding(horizontal = 4.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth(),
|
||||||
.heightIn(128.dp, 312.dp)
|
) {
|
||||||
.padding(horizontal = 4.dp),
|
items(filterItems) {
|
||||||
) {
|
FilterCheckbox(
|
||||||
items(filterItems) {
|
checked = it.enabled.value,
|
||||||
FilterCheckbox(
|
stringRes = it.stringRes,
|
||||||
checked = it.enabled.value,
|
) { it.enabled.value = !it.enabled.value }
|
||||||
stringRes = it.stringRes,
|
|
||||||
) { it.enabled.value = !it.enabled.value }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp),
|
|
||||||
horizontalArrangement = Arrangement.End
|
|
||||||
) {
|
|
||||||
TextButton(onClick = { onDismissRequest.invoke() }) {
|
|
||||||
Text(text = "Отмена")
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.padding(horizontal = 8.dp))
|
|
||||||
Button(onClick = { onApply.invoke(filterItems) }) {
|
|
||||||
Text(text = stringResource(id = R.string.apply))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Button(
|
||||||
|
onClick = { onApply.invoke(filterItems) },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 16.dp, horizontal = 64.dp)
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(id = R.string.apply))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun PreviewFilterSensorsDialog() {
|
fun PreviewSensorFilterBottomSheet() {
|
||||||
NarodMonTheme {
|
NarodMonTheme {
|
||||||
FilterSensorsDialog({}) {}
|
FilterSensorsBottomSheet({}) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package ru.nm17.narodmon.ui.bottomSheets
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.SheetState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import ru.nm17.narodmon.R
|
||||||
|
import ru.nm17.narodmon.ui.entities.SensorSortingUiEntity
|
||||||
|
import ru.nm17.narodmon.ui.entities.SortingTypes
|
||||||
|
import ru.nm17.narodmon.ui.pages.FilterRadioButton
|
||||||
|
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun SortSensorsBottomSheet(
|
||||||
|
onApply: (sortingType: SortingTypes) -> Unit,
|
||||||
|
onDismissRequest: () -> Unit
|
||||||
|
) {
|
||||||
|
var sortingType by remember { mutableStateOf(SortingTypes.DISTANCE) }
|
||||||
|
|
||||||
|
val sortingTypes = remember {
|
||||||
|
listOf(
|
||||||
|
SensorSortingUiEntity(R.string.sort_by_name, SortingTypes.NAME),
|
||||||
|
SensorSortingUiEntity(R.string.sort_by_name_desc, SortingTypes.NAME_DESC),
|
||||||
|
SensorSortingUiEntity(R.string.sort_by_distance, SortingTypes.DISTANCE),
|
||||||
|
SensorSortingUiEntity(R.string.sort_by_distance_desc, SortingTypes.DISTANCE_DESC),
|
||||||
|
SensorSortingUiEntity(R.string.sort_by_type, SortingTypes.TYPE),
|
||||||
|
SensorSortingUiEntity(R.string.sort_by_type_desc, SortingTypes.TYPE_DESC),
|
||||||
|
SensorSortingUiEntity(R.string.sort_update_time, SortingTypes.UPD_TIME),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = { onDismissRequest.invoke() },
|
||||||
|
sheetState = SheetState(true)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.sensors_sort_title),
|
||||||
|
fontSize = 24.sp,
|
||||||
|
fontWeight = FontWeight(500),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 4.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
items(sortingTypes) {
|
||||||
|
FilterRadioButton(
|
||||||
|
selected = (sortingType == it.sortingType),
|
||||||
|
onClick = { sortingType = it.sortingType },
|
||||||
|
stringRes = it.stringRes,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 64.dp, vertical = 8.dp),
|
||||||
|
onClick = {
|
||||||
|
onApply.invoke(sortingType)
|
||||||
|
}) {
|
||||||
|
Text(text = stringResource(R.string.apply))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun PreviewSortBottomSheet() {
|
||||||
|
NarodMonTheme {
|
||||||
|
SortSensorsBottomSheet(onApply = {}) {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,122 +0,0 @@
|
||||||
package ru.nm17.narodmon.ui.dialogs
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
|
||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.AlertDialogDefaults
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import ru.nm17.narodmon.R
|
|
||||||
import ru.nm17.narodmon.ui.elements.FilterRadioButton
|
|
||||||
import ru.nm17.narodmon.ui.entities.SensorSortingUiEntity
|
|
||||||
import ru.nm17.narodmon.ui.entities.SortingTypes
|
|
||||||
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun SortSensorsDialog(
|
|
||||||
selected: SensorSortingUiEntity,
|
|
||||||
sortingTypes: List<SensorSortingUiEntity>? = null,
|
|
||||||
onApply: (sortingType: SensorSortingUiEntity) -> Unit,
|
|
||||||
onDismissRequest: () -> Unit
|
|
||||||
) {
|
|
||||||
var selectedType by remember {
|
|
||||||
mutableStateOf(selected)
|
|
||||||
}
|
|
||||||
val sensorsSortingTypes = listOf(
|
|
||||||
SensorSortingUiEntity(R.string.sort_by_name, SortingTypes.NAME),
|
|
||||||
SensorSortingUiEntity(R.string.sort_by_name_desc, SortingTypes.NAME_DESC),
|
|
||||||
SensorSortingUiEntity(R.string.sort_by_distance, SortingTypes.DISTANCE),
|
|
||||||
SensorSortingUiEntity(R.string.sort_by_distance_desc, SortingTypes.DISTANCE_DESC),
|
|
||||||
SensorSortingUiEntity(R.string.sort_by_type, SortingTypes.TYPE),
|
|
||||||
SensorSortingUiEntity(R.string.sort_by_type_desc, SortingTypes.TYPE_DESC),
|
|
||||||
SensorSortingUiEntity(R.string.sort_update_time, SortingTypes.UPD_TIME),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = {
|
|
||||||
onDismissRequest.invoke()
|
|
||||||
}) {
|
|
||||||
Surface(
|
|
||||||
modifier = Modifier
|
|
||||||
.wrapContentWidth()
|
|
||||||
.wrapContentHeight(),
|
|
||||||
shape = MaterialTheme.shapes.large,
|
|
||||||
tonalElevation = AlertDialogDefaults.TonalElevation
|
|
||||||
) {
|
|
||||||
Column(modifier = Modifier.padding(vertical = 16.dp)) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(id = R.string.sensors_sort_title),
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
.fillMaxWidth()
|
|
||||||
)
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 4.dp),
|
|
||||||
) {
|
|
||||||
items(sortingTypes ?: sensorsSortingTypes) {
|
|
||||||
FilterRadioButton(
|
|
||||||
selected = (selectedType == it),
|
|
||||||
onClick = { selectedType = it },
|
|
||||||
stringRes = it.stringRes,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp),
|
|
||||||
horizontalArrangement = Arrangement.End
|
|
||||||
) {
|
|
||||||
TextButton(onClick = { onDismissRequest.invoke() }) {
|
|
||||||
Text(text = "Отмена")
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.padding(horizontal = 8.dp))
|
|
||||||
Button(onClick = { onApply.invoke(selectedType) }) {
|
|
||||||
Text(text = stringResource(id = R.string.apply))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun SortSensorsPreview() {
|
|
||||||
NarodMonTheme {
|
|
||||||
SortSensorsDialog(
|
|
||||||
SensorSortingUiEntity(R.string.sort_by_distance, SortingTypes.DISTANCE),
|
|
||||||
onApply = {}) {}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
|
||||||
package ru.nm17.narodmon.ui.dialogs
|
package ru.nm17.narodmon.ui.elements
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
@ -20,9 +20,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import ru.nm17.narodmon.R
|
import ru.nm17.narodmon.R
|
||||||
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@ -85,14 +83,4 @@ fun AgreementDialog(modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun PreviewAgreementDialog(){
|
|
||||||
NarodMonTheme {
|
|
||||||
AgreementDialog {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
package ru.nm17.narodmon.ui.elements
|
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.material3.Checkbox
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
|
|
||||||
@ExperimentalMaterial3Api
|
|
||||||
@Composable
|
|
||||||
fun FilterCheckbox(checked: Boolean, stringRes: Int, onCheckedChange: () -> Unit) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable { onCheckedChange() }
|
|
||||||
) {
|
|
||||||
Checkbox(
|
|
||||||
checked = checked,
|
|
||||||
onCheckedChange = { onCheckedChange() },
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = stringResource(id = stringRes),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package ru.nm17.narodmon.ui.elements
|
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.RadioButton
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
|
|
||||||
@ExperimentalMaterial3Api
|
|
||||||
@Composable
|
|
||||||
fun FilterRadioButton(selected: Boolean, onClick: () -> Unit, stringRes: Int) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable { onClick.invoke() }
|
|
||||||
) {
|
|
||||||
RadioButton(
|
|
||||||
selected = selected,
|
|
||||||
onClick = onClick,
|
|
||||||
)
|
|
||||||
Text(text = stringResource(id = stringRes))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package ru.nm17.narodmon.ui.elements
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material3.ElevatedCard
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.ListItem
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import ru.nm17.narodmon.ui.entities.SensorEntity
|
|
||||||
import ru.nm17.narodmon.ui.iosevkaFamily
|
|
||||||
|
|
||||||
@ExperimentalMaterial3Api
|
|
||||||
@Composable
|
|
||||||
fun SensorItem(sensorEntity: SensorEntity) {
|
|
||||||
ListItem(
|
|
||||||
overlineContent = { Text(text = "${sensorEntity.deviceName} от ${sensorEntity.deviceOwner}") },
|
|
||||||
headlineContent = { Text(text = sensorEntity.type.name) },
|
|
||||||
supportingContent = { Text(text = sensorEntity.name) },
|
|
||||||
trailingContent = {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.End,
|
|
||||||
) {
|
|
||||||
Text(text = "${sensorEntity.distance} km")
|
|
||||||
Spacer(modifier = Modifier.size(2.dp))
|
|
||||||
ElevatedCard(
|
|
||||||
shape = RectangleShape,
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "${sensorEntity.value} ${sensorEntity.unit}",
|
|
||||||
fontFamily = iosevkaFamily,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
fontSize = 14.sp,
|
|
||||||
modifier = Modifier.padding(horizontal = 3.dp, vertical = 1.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun PreviewSensorItem() {
|
|
||||||
// SensorItem(SensorEntity(0, Se))
|
|
||||||
}
|
|
|
@ -13,7 +13,6 @@ import io.ktor.client.request.get
|
||||||
import io.ktor.client.statement.bodyAsChannel
|
import io.ktor.client.statement.bodyAsChannel
|
||||||
import io.ktor.utils.io.jvm.javaio.toInputStream
|
import io.ktor.utils.io.jvm.javaio.toInputStream
|
||||||
import ovh.plrapps.mapcompose.api.addLayer
|
import ovh.plrapps.mapcompose.api.addLayer
|
||||||
import ovh.plrapps.mapcompose.api.onTouchDown
|
|
||||||
import ovh.plrapps.mapcompose.api.scale
|
import ovh.plrapps.mapcompose.api.scale
|
||||||
import ovh.plrapps.mapcompose.api.setScroll
|
import ovh.plrapps.mapcompose.api.setScroll
|
||||||
import ovh.plrapps.mapcompose.core.TileStreamProvider
|
import ovh.plrapps.mapcompose.core.TileStreamProvider
|
||||||
|
@ -34,7 +33,7 @@ suspend fun requestTile(row: Int, col: Int, zoom: Int): InputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TileMap(modifier: Modifier = Modifier, onTap: () -> Unit) {
|
fun TileMap(modifier: Modifier = Modifier) {
|
||||||
val state by remember {
|
val state by remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
MapState(
|
MapState(
|
||||||
|
@ -44,9 +43,6 @@ fun TileMap(modifier: Modifier = Modifier, onTap: () -> Unit) {
|
||||||
workerCount = 16,
|
workerCount = 16,
|
||||||
).apply {
|
).apply {
|
||||||
addLayer(tileStreamProvider)
|
addLayer(tileStreamProvider)
|
||||||
onTouchDown {
|
|
||||||
onTap.invoke()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
247
app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt
Normal file
247
app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
package ru.nm17.narodmon.ui.pages
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.AssistChip
|
||||||
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.Divider
|
||||||
|
import androidx.compose.material3.ElevatedCard
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.RadioButton
|
||||||
|
import androidx.compose.material3.SearchBar
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import ru.nm17.narodmon.R
|
||||||
|
import ru.nm17.narodmon.db.entities.SensorType
|
||||||
|
import ru.nm17.narodmon.ui.bottomSheets.FilterSensorsBottomSheet
|
||||||
|
import ru.nm17.narodmon.ui.bottomSheets.SortSensorsBottomSheet
|
||||||
|
import ru.nm17.narodmon.ui.elements.GenericNavScaffold
|
||||||
|
import ru.nm17.narodmon.ui.elements.TileMap
|
||||||
|
import ru.nm17.narodmon.ui.entities.SensorEntity
|
||||||
|
import ru.nm17.narodmon.ui.entities.SensorFilterUiEntity
|
||||||
|
import ru.nm17.narodmon.ui.entities.SortingTypes
|
||||||
|
import ru.nm17.narodmon.ui.iosevkaFamily
|
||||||
|
|
||||||
|
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@Composable
|
||||||
|
fun SensorsPage(navController: NavController) {
|
||||||
|
var searchQuery by remember { mutableStateOf("") }
|
||||||
|
var searchActive by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
|
||||||
|
var sortingShow by remember { mutableStateOf(false) }
|
||||||
|
var sortingType by remember { mutableStateOf(SortingTypes.DISTANCE) }
|
||||||
|
|
||||||
|
var filterShow by remember { mutableStateOf(false) }
|
||||||
|
var filterMine by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
|
||||||
|
val sensorEntities = remember {
|
||||||
|
mutableListOf(
|
||||||
|
// TODO: загружать датчики с сервера. Этот список -- для макета
|
||||||
|
SensorEntity(
|
||||||
|
0,
|
||||||
|
SensorType(0, "temp", "C"),
|
||||||
|
"device0", 0,
|
||||||
|
"sensor0", favorite = true,
|
||||||
|
public = true, mine = false,
|
||||||
|
"Москва", 0.4,
|
||||||
|
20.0, "C", 1686142800,
|
||||||
|
),
|
||||||
|
SensorEntity(
|
||||||
|
1,
|
||||||
|
SensorType(4, "humidity", "%"),
|
||||||
|
"device1", 0,
|
||||||
|
"sensor1", favorite = true,
|
||||||
|
public = false, mine = false,
|
||||||
|
"Подмосковье", 1.1,
|
||||||
|
39.0, "%", 1686142800,
|
||||||
|
),
|
||||||
|
SensorEntity(
|
||||||
|
2,
|
||||||
|
SensorType(11, "wind speed", "m/s"),
|
||||||
|
"device2", 1,
|
||||||
|
"sensor2", favorite = false,
|
||||||
|
public = true, mine = true,
|
||||||
|
"Москва", 0.01,
|
||||||
|
3.2, "m/s", 1686142800,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val scrConfig = LocalConfiguration.current
|
||||||
|
val mapHeight = scrConfig.screenHeightDp / 3
|
||||||
|
|
||||||
|
GenericNavScaffold(
|
||||||
|
title = { Text(text = stringResource(R.string.sensors_page_title)) }
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(it)) {
|
||||||
|
|
||||||
|
TileMap(modifier = Modifier.height(mapHeight.dp))
|
||||||
|
|
||||||
|
SearchBar(
|
||||||
|
query = searchQuery,
|
||||||
|
active = searchActive,
|
||||||
|
onActiveChange = { active -> searchActive = active },
|
||||||
|
onQueryChange = { query -> searchQuery = query },
|
||||||
|
onSearch = { searchActive = false },
|
||||||
|
placeholder = { Text(stringResource(R.string.search)) },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = if (!searchActive) 8.dp else 0.dp)
|
||||||
|
) {}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp),
|
||||||
|
) {
|
||||||
|
AssistChip(
|
||||||
|
onClick = { filterShow = true },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_filter),
|
||||||
|
contentDescription = stringResource(id = R.string.sensors_filter)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { Text(text = stringResource(R.string.sensors_filter)) },
|
||||||
|
)
|
||||||
|
|
||||||
|
AssistChip(
|
||||||
|
onClick = { sortingShow = true },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_sort),
|
||||||
|
contentDescription = stringResource(id = R.string.sensors_sorting)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { Text(text = stringResource(R.string.sensors_sorting)) },
|
||||||
|
)
|
||||||
|
|
||||||
|
FilterChip(
|
||||||
|
selected = filterMine,
|
||||||
|
onClick = { filterMine = !filterMine },
|
||||||
|
label = { Text(text = stringResource(R.string.sensors_mine)) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxHeight(),
|
||||||
|
) {
|
||||||
|
items(sensorEntities) { sensor ->
|
||||||
|
SensorItem(sensor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterShow) {
|
||||||
|
FilterSensorsBottomSheet(
|
||||||
|
onApply = {
|
||||||
|
// TODO применение фильтров
|
||||||
|
filterShow = false
|
||||||
|
},
|
||||||
|
onDismissRequest = { filterShow = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sortingShow) {
|
||||||
|
SortSensorsBottomSheet(
|
||||||
|
onApply = { s ->
|
||||||
|
sortingType = s
|
||||||
|
sortingShow = false
|
||||||
|
},
|
||||||
|
onDismissRequest = { sortingShow = false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@Composable
|
||||||
|
fun SensorItem(sensorEntity: SensorEntity) {
|
||||||
|
ListItem(
|
||||||
|
overlineContent = { Text(text = "${sensorEntity.deviceName} от ${sensorEntity.deviceOwner}") },
|
||||||
|
headlineContent = { Text(text = sensorEntity.type.name) },
|
||||||
|
supportingContent = { Text(text = sensorEntity.name) },
|
||||||
|
trailingContent = {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.End,
|
||||||
|
) {
|
||||||
|
Text(text = "${sensorEntity.distance} km")
|
||||||
|
ElevatedCard(
|
||||||
|
shape = RectangleShape,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "${sensorEntity.value} ${sensorEntity.unit}",
|
||||||
|
fontFamily = iosevkaFamily,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
modifier = Modifier.padding(horizontal = 3.dp, vertical = 1.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@Composable
|
||||||
|
fun FilterCheckbox(checked: Boolean, stringRes: Int, onCheckedChange: () -> Unit) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { onCheckedChange() }
|
||||||
|
) {
|
||||||
|
Checkbox(
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = { onCheckedChange() },
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = stringRes),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@Composable
|
||||||
|
fun FilterRadioButton(selected: Boolean, onClick: () -> Unit, stringRes: Int) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { onClick.invoke() }
|
||||||
|
) {
|
||||||
|
RadioButton(
|
||||||
|
selected = selected,
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
Text(text = stringResource(id = stringRes))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,282 +0,0 @@
|
||||||
package ru.nm17.narodmon.ui.sensorsScreen
|
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
|
||||||
import androidx.compose.material.icons.rounded.ArrowDropDown
|
|
||||||
import androidx.compose.material.icons.rounded.Check
|
|
||||||
import androidx.compose.material.icons.rounded.Person
|
|
||||||
import androidx.compose.material3.BottomSheetScaffold
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.FilterChip
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.SearchBar
|
|
||||||
import androidx.compose.material3.SearchBarDefaults
|
|
||||||
import androidx.compose.material3.SheetValue
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextField
|
|
||||||
import androidx.compose.material3.TextFieldColors
|
|
||||||
import androidx.compose.material3.TextFieldDefaults
|
|
||||||
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import ovh.plrapps.mapcompose.core.debounce
|
|
||||||
import ru.nm17.narodmon.R
|
|
||||||
import ru.nm17.narodmon.db.entities.SensorType
|
|
||||||
import ru.nm17.narodmon.ui.dialogs.FilterSensorsDialog
|
|
||||||
import ru.nm17.narodmon.ui.elements.SensorItem
|
|
||||||
import ru.nm17.narodmon.ui.dialogs.SortSensorsDialog
|
|
||||||
import ru.nm17.narodmon.ui.elements.TileMap
|
|
||||||
import ru.nm17.narodmon.ui.entities.SensorEntity
|
|
||||||
import ru.nm17.narodmon.ui.entities.SensorSortingUiEntity
|
|
||||||
import ru.nm17.narodmon.ui.entities.SortingTypes
|
|
||||||
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
|
||||||
import ru.nm17.narodmon.ui.toChipTitle
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
|
|
||||||
@Composable
|
|
||||||
fun SensorsScreen(navController: NavController) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
var searchQuery by remember { mutableStateOf("") }
|
|
||||||
var searchActive by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
val scaffoldState = rememberBottomSheetScaffoldState()
|
|
||||||
val sensorEntities = remember {
|
|
||||||
mutableListOf(
|
|
||||||
// TODO: загружать датчики с сервера. Этот список -- для макета
|
|
||||||
SensorEntity(
|
|
||||||
0,
|
|
||||||
SensorType(0, "temp", "C"),
|
|
||||||
"device0", 0,
|
|
||||||
"sensor0", favorite = true,
|
|
||||||
public = true, mine = false,
|
|
||||||
"Москва", 0.4,
|
|
||||||
20.0, "C", 1686142800,
|
|
||||||
),
|
|
||||||
SensorEntity(
|
|
||||||
1,
|
|
||||||
SensorType(4, "humidity", "%"),
|
|
||||||
"device1", 0,
|
|
||||||
"sensor1", favorite = true,
|
|
||||||
public = false, mine = false,
|
|
||||||
"Подмосковье", 1.1,
|
|
||||||
39.0, "%", 1686142800,
|
|
||||||
),
|
|
||||||
SensorEntity(
|
|
||||||
2,
|
|
||||||
SensorType(11, "wind speed", "m/s"),
|
|
||||||
"device2", 1,
|
|
||||||
"sensor2", favorite = false,
|
|
||||||
public = true, mine = true,
|
|
||||||
"Москва", 0.01,
|
|
||||||
3.2, "m/s", 1686142800,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var filterShow by remember { mutableStateOf(false) }
|
|
||||||
var filterMine by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
var sortingShow by remember { mutableStateOf(false) }
|
|
||||||
var sortingType by remember {
|
|
||||||
mutableStateOf(
|
|
||||||
SensorSortingUiEntity(R.string.sort_by_distance, SortingTypes.DISTANCE)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var sheetHeight by remember {
|
|
||||||
mutableStateOf(SheetHeight.ExtraExpanded)
|
|
||||||
}
|
|
||||||
|
|
||||||
BottomSheetScaffold(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
sheetPeekHeight = when (sheetHeight) {
|
|
||||||
SheetHeight.ExtraExpanded -> 256.dp
|
|
||||||
SheetHeight.Expanded -> 128.dp
|
|
||||||
SheetHeight.Hidden -> 0.dp
|
|
||||||
},
|
|
||||||
scaffoldState = scaffoldState,
|
|
||||||
sheetContent = {
|
|
||||||
AnimatedVisibility(visible = scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = searchQuery,
|
|
||||||
onValueChange = { searchQuery = it },
|
|
||||||
placeholder = { Text(stringResource(R.string.search)) },
|
|
||||||
shape = SearchBarDefaults.inputFieldShape,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 8.dp, vertical = 8.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
LazyRow(
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterHorizontally),
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp),
|
|
||||||
) {
|
|
||||||
item {
|
|
||||||
FilterChip(
|
|
||||||
selected = false,
|
|
||||||
onClick = { filterShow = true },
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(id = R.drawable.ic_filter),
|
|
||||||
contentDescription = stringResource(id = R.string.sensors_filter)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
trailingIcon = {
|
|
||||||
// Icon(
|
|
||||||
// Icons.Filled.ArrowDropDown,
|
|
||||||
// "",
|
|
||||||
// tint = MaterialTheme.colorScheme.onBackground
|
|
||||||
// )
|
|
||||||
},
|
|
||||||
|
|
||||||
label = { Text(text = stringResource(R.string.sensors_filter)) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
FilterChip(
|
|
||||||
selected = sortingType.sortingType != SortingTypes.DISTANCE,
|
|
||||||
onClick = { sortingShow = true },
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(id = R.drawable.ic_sort),
|
|
||||||
contentDescription = stringResource(id = R.string.sensors_sorting)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
trailingIcon = {
|
|
||||||
// Icon(
|
|
||||||
// Icons.Filled.ArrowDropDown,
|
|
||||||
// "",
|
|
||||||
// tint = MaterialTheme.colorScheme.onBackground
|
|
||||||
// )
|
|
||||||
},
|
|
||||||
label = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(
|
|
||||||
if (sortingType.sortingType == SortingTypes.DISTANCE) R.string.sensors_sorting
|
|
||||||
else sortingType.stringRes
|
|
||||||
).toChipTitle(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
FilterChip(
|
|
||||||
selected = filterMine,
|
|
||||||
onClick = { filterMine = !filterMine },
|
|
||||||
leadingIcon = {
|
|
||||||
if (filterMine) {
|
|
||||||
Icon(
|
|
||||||
Icons.Rounded.Check,
|
|
||||||
contentDescription = ""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label = { Text(text = stringResource(R.string.sensors_mine)) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.fillMaxHeight(),
|
|
||||||
) {
|
|
||||||
items(sensorEntities) { sensor ->
|
|
||||||
SensorItem(sensor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
|
||||||
SearchBar(
|
|
||||||
query = searchQuery,
|
|
||||||
active = searchActive,
|
|
||||||
onActiveChange = { active ->
|
|
||||||
searchActive = active
|
|
||||||
sheetHeight = if (active) SheetHeight.Hidden else SheetHeight.ExtraExpanded
|
|
||||||
},
|
|
||||||
onQueryChange = { query -> searchQuery = query },
|
|
||||||
onSearch = { searchActive = false },
|
|
||||||
placeholder = { Text(stringResource(R.string.search)) },
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(
|
|
||||||
horizontal = if (!searchActive) 8.dp else 0.dp,
|
|
||||||
vertical = if (!searchActive) 16.dp else 0.dp
|
|
||||||
)
|
|
||||||
) {}
|
|
||||||
|
|
||||||
TileMap(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
) {
|
|
||||||
sheetHeight =
|
|
||||||
SheetHeight.Expanded // TODO придумать, чтобы менялось на SheetHeight.ExtraExpanded после взаимодействия с картой
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sortingShow) {
|
|
||||||
SortSensorsDialog(
|
|
||||||
sortingType,
|
|
||||||
onApply = {
|
|
||||||
sortingType = it
|
|
||||||
sortingShow = false
|
|
||||||
},
|
|
||||||
onDismissRequest = {
|
|
||||||
sortingShow = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (filterShow) {
|
|
||||||
FilterSensorsDialog(
|
|
||||||
onApply = {
|
|
||||||
// TODO применение фильтров
|
|
||||||
filterShow = false
|
|
||||||
},
|
|
||||||
onDismissRequest = { filterShow = false }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun PreviewNewSensors() {
|
|
||||||
NarodMonTheme {
|
|
||||||
SensorsScreen(rememberNavController())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package ru.nm17.narodmon.ui.sensorsScreen
|
|
||||||
|
|
||||||
enum class SheetHeight {
|
|
||||||
ExtraExpanded,
|
|
||||||
Expanded,
|
|
||||||
Hidden
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@
|
||||||
<string name="sensors_page_title">Сенсоры</string>
|
<string name="sensors_page_title">Сенсоры</string>
|
||||||
<string name="waiting_for_user_agreement">Ожидаю соглашение пользователя</string>
|
<string name="waiting_for_user_agreement">Ожидаю соглашение пользователя</string>
|
||||||
<string name="search">Поиск</string>
|
<string name="search">Поиск</string>
|
||||||
<string name="sensors_filter">Тип датчиков</string>
|
<string name="sensors_filter">Фильтр</string>
|
||||||
<string name="sensors_sorting">Сортировка</string>
|
<string name="sensors_sorting">Сортировка</string>
|
||||||
<string name="sensors_mine">Мои датчики</string>
|
<string name="sensors_mine">Мои датчики</string>
|
||||||
<string name="filter_temp_dew_point">Температура точки росы</string>
|
<string name="filter_temp_dew_point">Температура точки росы</string>
|
||||||
|
@ -51,5 +51,4 @@
|
||||||
<string name="sort_update_time">По времени обновления</string>
|
<string name="sort_update_time">По времени обновления</string>
|
||||||
<string name="apply">Применить</string>
|
<string name="apply">Применить</string>
|
||||||
<string name="sort_by_distance_desc">От дальних к ближним</string>
|
<string name="sort_by_distance_desc">От дальних к ближним</string>
|
||||||
<string name="cancel1">Отменить</string>
|
|
||||||
</resources>
|
</resources>
|
Loading…
Add table
Reference in a new issue