Новый экран датчиков
This commit is contained in:
parent
1811a22a5a
commit
8c1c35afdd
10 changed files with 534 additions and 414 deletions
|
@ -1,4 +1,5 @@
|
|||
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class,
|
||||
@file:OptIn(
|
||||
ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class,
|
||||
ExperimentalMaterial3Api::class
|
||||
)
|
||||
|
||||
|
@ -7,12 +8,20 @@ package ru.nm17.narodmon
|
|||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
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.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -24,8 +33,9 @@ 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.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
|
@ -36,8 +46,8 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
import ru.nm17.narodmon.db.AppDatabase
|
||||
import ru.nm17.narodmon.db.entities.KVSetting
|
||||
import ru.nm17.narodmon.ui.elements.AgreementDialog
|
||||
import ru.nm17.narodmon.ui.pages.SensorsPage
|
||||
import ru.nm17.narodmon.ui.dialogs.AgreementDialog
|
||||
import ru.nm17.narodmon.ui.sensorsScreen.SensorsScreen
|
||||
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
||||
|
||||
|
||||
|
@ -46,18 +56,39 @@ import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
|||
fun AppNavHost() {
|
||||
val navController = rememberNavController()
|
||||
val coScope = rememberCoroutineScope()
|
||||
|
||||
NavHost(navController = navController, startDestination = "sensors") {
|
||||
composable("agreement") {
|
||||
|
||||
}
|
||||
|
||||
composable("sensors") {
|
||||
SensorsPage(navController)
|
||||
}
|
||||
Scaffold(bottomBar = {
|
||||
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() {
|
||||
|
@ -69,20 +100,20 @@ class MainActivity : ComponentActivity() {
|
|||
AppDatabase::class.java, "data"
|
||||
).build()
|
||||
|
||||
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
||||
|
||||
val sharedPreferences = EncryptedSharedPreferences.create(
|
||||
"secret_shared_prefs",
|
||||
masterKeyAlias,
|
||||
createDeviceProtectedStorageContext(),
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
)
|
||||
// val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
||||
//
|
||||
// val sharedPreferences = EncryptedSharedPreferences.create(
|
||||
// "secret_shared_prefs",
|
||||
// masterKeyAlias,
|
||||
// createDeviceProtectedStorageContext(),
|
||||
// EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
// EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
// )
|
||||
|
||||
// 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 {
|
||||
|
@ -120,9 +151,9 @@ class MainActivity : ComponentActivity() {
|
|||
Text(text = stringResource(R.string.waiting_for_user_agreement))
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
AppNavHost()
|
||||
|
||||
}
|
||||
|
||||
// A surface container using the 'background' color from the theme
|
||||
|
|
9
app/src/main/java/ru/nm17/narodmon/ui/Utils.kt
Normal file
9
app/src/main/java/ru/nm17/narodmon/ui/Utils.kt
Normal file
|
@ -0,0 +1,9 @@
|
|||
package ru.nm17.narodmon.ui
|
||||
|
||||
fun String.toChipTitle(): String {
|
||||
return if (length >= 20) {
|
||||
this.slice(0..16) + ".."
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
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,37 +1,37 @@
|
|||
package ru.nm17.narodmon.ui.bottomSheets
|
||||
package ru.nm17.narodmon.ui.dialogs
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.SheetValue
|
||||
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.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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.elements.FilterCheckbox
|
||||
import ru.nm17.narodmon.ui.entities.SensorFilterUiEntity
|
||||
import ru.nm17.narodmon.ui.pages.FilterCheckbox
|
||||
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun FilterSensorsBottomSheet(
|
||||
fun FilterSensorsDialog(
|
||||
onApply: (filters: List<SensorFilterUiEntity>) -> Unit,
|
||||
onDismissRequest: () -> Unit
|
||||
) {
|
||||
|
@ -67,53 +67,61 @@ fun FilterSensorsBottomSheet(
|
|||
)
|
||||
}
|
||||
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { onDismissRequest.invoke() },
|
||||
sheetState = SheetState(true)
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.sensors_filter_title),
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight(500),
|
||||
)
|
||||
}
|
||||
Box(contentAlignment = Alignment.BottomCenter) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 4.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
items(filterItems) {
|
||||
FilterCheckbox(
|
||||
checked = it.enabled.value,
|
||||
stringRes = it.stringRes,
|
||||
) { it.enabled.value = !it.enabled.value }
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
onDismissRequest.invoke()
|
||||
}) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.large,
|
||||
tonalElevation = AlertDialogDefaults.TonalElevation
|
||||
) {
|
||||
Column(modifier = Modifier.padding(vertical = 16.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.sensors_filter_title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(128.dp, 312.dp)
|
||||
.padding(horizontal = 4.dp),
|
||||
) {
|
||||
items(filterItems) {
|
||||
FilterCheckbox(
|
||||
checked = 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
|
||||
@Composable
|
||||
fun PreviewSensorFilterBottomSheet() {
|
||||
fun PreviewFilterSensorsDialog() {
|
||||
NarodMonTheme {
|
||||
FilterSensorsBottomSheet({}) {
|
||||
|
||||
}
|
||||
FilterSensorsDialog({}) {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
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 = {}) {}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import io.ktor.client.request.get
|
|||
import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.utils.io.jvm.javaio.toInputStream
|
||||
import ovh.plrapps.mapcompose.api.addLayer
|
||||
import ovh.plrapps.mapcompose.api.onTouchDown
|
||||
import ovh.plrapps.mapcompose.api.scale
|
||||
import ovh.plrapps.mapcompose.api.setScroll
|
||||
import ovh.plrapps.mapcompose.core.TileStreamProvider
|
||||
|
@ -33,7 +34,7 @@ suspend fun requestTile(row: Int, col: Int, zoom: Int): InputStream {
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun TileMap(modifier: Modifier = Modifier) {
|
||||
fun TileMap(modifier: Modifier = Modifier, onTap: () -> Unit) {
|
||||
val state by remember {
|
||||
mutableStateOf(
|
||||
MapState(
|
||||
|
@ -43,6 +44,9 @@ fun TileMap(modifier: Modifier = Modifier) {
|
|||
workerCount = 16,
|
||||
).apply {
|
||||
addLayer(tileStreamProvider)
|
||||
onTouchDown {
|
||||
onTap.invoke()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,247 +0,0 @@
|
|||
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))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
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())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package ru.nm17.narodmon.ui.sensorsScreen
|
||||
|
||||
enum class SheetHeight {
|
||||
ExtraExpanded,
|
||||
Expanded,
|
||||
Hidden
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
<string name="sensors_page_title">Сенсоры</string>
|
||||
<string name="waiting_for_user_agreement">Ожидаю соглашение пользователя</string>
|
||||
<string name="search">Поиск</string>
|
||||
<string name="sensors_filter">Фильтр</string>
|
||||
<string name="sensors_filter">Тип датчиков</string>
|
||||
<string name="sensors_sorting">Сортировка</string>
|
||||
<string name="sensors_mine">Мои датчики</string>
|
||||
<string name="filter_temp_dew_point">Температура точки росы</string>
|
||||
|
@ -51,4 +51,5 @@
|
|||
<string name="sort_update_time">По времени обновления</string>
|
||||
<string name="apply">Применить</string>
|
||||
<string name="sort_by_distance_desc">От дальних к ближним</string>
|
||||
<string name="cancel1">Отменить</string>
|
||||
</resources>
|
Loading…
Add table
Reference in a new issue