From 0935cd88d434f26fe958aebf237e65ea703b7b8c Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Tue, 6 Jun 2023 14:57:49 +0400 Subject: [PATCH 01/28] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BD=D1=91?= =?UTF-8?q?=D1=81=20=D0=BA=D0=B0=D1=80=D1=82=D1=83=20=D0=B2=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=20=D0=B2=D0=BC=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=B5=20=D1=81=D0=BE=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ru/nm17/narodmon/ui/elements/TileMap.kt | 57 +++++++++++++++++++ .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 16 +----- .../narodmon/ui/viewmodel/MapViewModel.kt | 39 ------------- 3 files changed, 59 insertions(+), 53 deletions(-) create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/elements/TileMap.kt delete mode 100644 app/src/main/java/ru/nm17/narodmon/ui/viewmodel/MapViewModel.kt diff --git a/app/src/main/java/ru/nm17/narodmon/ui/elements/TileMap.kt b/app/src/main/java/ru/nm17/narodmon/ui/elements/TileMap.kt new file mode 100644 index 0000000..6f20744 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/elements/TileMap.kt @@ -0,0 +1,57 @@ +package ru.nm17.narodmon.ui.elements + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import io.ktor.client.HttpClient +import io.ktor.client.engine.cio.CIO +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.scale +import ovh.plrapps.mapcompose.api.setScroll +import ovh.plrapps.mapcompose.core.TileStreamProvider +import ovh.plrapps.mapcompose.ui.MapUI +import ovh.plrapps.mapcompose.ui.state.MapState +import java.io.InputStream + +const val mapSize = 32768 + +val client = HttpClient(CIO) +val tileStreamProvider = TileStreamProvider { row, col, zoom -> + requestTile(row, col, zoom) +} + +suspend fun requestTile(row: Int, col: Int, zoom: Int): InputStream { + val response = client.get("https://tile.openstreetmap.org/${zoom}/${col}/${row}.png") + return response.bodyAsChannel().toInputStream() +} + +@Composable +fun TileMap(modifier: Modifier = Modifier) { + val state by remember { + mutableStateOf( + MapState( + levelCount = 8, + fullWidth = mapSize, + fullHeight = mapSize, + workerCount = 16, + ).apply { + addLayer(tileStreamProvider) + } + ) + } + + LaunchedEffect(state) { + // TODO: Подгружать сохранённую позицию + state.setScroll(Offset(28702.6F, 14787.6F)) + state.scale = 1.4658884F + } + + MapUI(modifier = modifier, state = state) +} \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 6bd37c1..8ff760f 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -9,23 +9,18 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import ovh.plrapps.mapcompose.api.scale -import ovh.plrapps.mapcompose.api.setScroll -import ovh.plrapps.mapcompose.ui.MapUI import ru.nm17.narodmon.R import ru.nm17.narodmon.ui.elements.GenericNavScaffold -import ru.nm17.narodmon.ui.viewmodel.MapViewModel +import ru.nm17.narodmon.ui.elements.TileMap enum class SensorsFilter { All, Thermometer, Camera, @@ -34,24 +29,17 @@ enum class SensorsFilter { @ExperimentalMaterial3Api @Composable fun SensorsPage(navController: NavController) { - val mapVM by remember { mutableStateOf(MapViewModel()) } var filter by remember { mutableStateOf(SensorsFilter.All) } val scrConfig = LocalConfiguration.current val mapHeight = scrConfig.screenHeightDp / 3 - - LaunchedEffect(mapVM) { - // TODO: Подгружать сохранённую позицию - mapVM.state.setScroll(Offset(28702.6F, 14787.6F)) - mapVM.state.scale = 1.4658884F - } GenericNavScaffold( title = { Text(text = stringResource(R.string.sensors_page_title)) } ) { Column(modifier = Modifier.padding(it)) { - MapUI(state = mapVM.state, modifier = Modifier.height(mapHeight.dp)) + TileMap(modifier = Modifier.height(mapHeight.dp)) Row( modifier = Modifier.padding(horizontal = 8.dp), diff --git a/app/src/main/java/ru/nm17/narodmon/ui/viewmodel/MapViewModel.kt b/app/src/main/java/ru/nm17/narodmon/ui/viewmodel/MapViewModel.kt deleted file mode 100644 index 4d330ba..0000000 --- a/app/src/main/java/ru/nm17/narodmon/ui/viewmodel/MapViewModel.kt +++ /dev/null @@ -1,39 +0,0 @@ -package ru.nm17.narodmon.ui.viewmodel - -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import io.ktor.client.HttpClient -import io.ktor.client.engine.cio.CIO -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.core.TileStreamProvider -import ovh.plrapps.mapcompose.ui.state.MapState -import java.io.InputStream - -class MapViewModel : ViewModel() { - private val client = HttpClient(CIO) - - private val tileStreamProvider = TileStreamProvider { row, col, zoom -> - requestTile(row, col, zoom) - } - - private val mapSize = 32768 - val state: MapState by mutableStateOf( - MapState( - levelCount = 8, - fullWidth = mapSize, - fullHeight = mapSize, - workerCount = 16, - ).apply { - addLayer(tileStreamProvider) - } - ) - - private suspend fun requestTile(row: Int, col: Int, zoom: Int): InputStream { - val response = client.get("https://tile.openstreetmap.org/${zoom}/${col}/${row}.png") - return response.bodyAsChannel().toInputStream() - } -} \ No newline at end of file From 2b2a2c2f5ae1959115daa7c1db7c574ebfa07cd4 Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Tue, 6 Jun 2023 15:33:43 +0400 Subject: [PATCH 02/28] =?UTF-8?q?IDE=20=D0=BD=D0=B5=20=D0=B2=D0=B8=D0=B4?= =?UTF-8?q?=D0=B8=D1=82=20@file:OptIn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/ru/nm17/narodmon/MainActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/ru/nm17/narodmon/MainActivity.kt b/app/src/main/java/ru/nm17/narodmon/MainActivity.kt index 63b65e5..b673692 100644 --- a/app/src/main/java/ru/nm17/narodmon/MainActivity.kt +++ b/app/src/main/java/ru/nm17/narodmon/MainActivity.kt @@ -41,6 +41,7 @@ import ru.nm17.narodmon.ui.pages.SensorsPage import ru.nm17.narodmon.ui.theme.NarodMonTheme +@OptIn(ExperimentalMaterial3Api::class) @Composable fun AppNavHost() { val navController = rememberNavController() From 06bc8a839e8b9db20ab03fac3196e0c4ce555878 Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Tue, 6 Jun 2023 15:50:31 +0400 Subject: [PATCH 03/28] Fix: java.security.cert.CertificateException --- app/src/main/java/ru/nm17/narodmon/ui/elements/TileMap.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/elements/TileMap.kt b/app/src/main/java/ru/nm17/narodmon/ui/elements/TileMap.kt index 6f20744..3da671e 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/elements/TileMap.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/elements/TileMap.kt @@ -8,7 +8,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import io.ktor.client.HttpClient -import io.ktor.client.engine.cio.CIO +import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.request.get import io.ktor.client.statement.bodyAsChannel import io.ktor.utils.io.jvm.javaio.toInputStream @@ -22,7 +22,7 @@ import java.io.InputStream const val mapSize = 32768 -val client = HttpClient(CIO) +val client = HttpClient(OkHttp) val tileStreamProvider = TileStreamProvider { row, col, zoom -> requestTile(row, col, zoom) } From 1334014c04cacaebd34166cbd2801fada608317a Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Tue, 6 Jun 2023 15:50:55 +0400 Subject: [PATCH 04/28] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=20UI=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B0=20=D1=81=D0=B5=D0=BD=D1=81=D0=BE=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 57 +++++++------------ app/src/main/res/values/strings.xml | 2 + 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 8ff760f..d08d804 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -3,10 +3,12 @@ package ru.nm17.narodmon.ui.pages import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.material3.AssistChip import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilterChip +import androidx.compose.material3.SearchBar import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -29,7 +31,8 @@ enum class SensorsFilter { @ExperimentalMaterial3Api @Composable fun SensorsPage(navController: NavController) { - var filter by remember { mutableStateOf(SensorsFilter.All) } + var searchQuery by remember { mutableStateOf("") } + var searchActive by remember { mutableStateOf(false) } val scrConfig = LocalConfiguration.current val mapHeight = scrConfig.screenHeightDp / 3 @@ -41,45 +44,27 @@ fun SensorsPage(navController: NavController) { 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 + //.padding(horizontal = 8.dp) + .fillMaxWidth() + ) {} + Row( - modifier = Modifier.padding(horizontal = 8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(horizontal = 8.dp), ) { - SensorsFilterChip( - name = stringResource(R.string.sensors_filter_all), - checkFilter = { filter == SensorsFilter.All }, - updateFilter = { filter = SensorsFilter.All }, - ) - - SensorsFilterChip( - name = stringResource(R.string.sensors_filter_temp), - checkFilter = { filter == SensorsFilter.Thermometer }, - updateFilter = { filter = SensorsFilter.Thermometer }, - ) - - SensorsFilterChip( - name = stringResource(R.string.sensors_filter_camera), - checkFilter = { filter == SensorsFilter.Camera }, - updateFilter = { filter = SensorsFilter.Camera }, + AssistChip( + onClick = { }, + label = { Text(text = stringResource(R.string.sensors_filter)) }, ) } - - //Text(mapVM.state.scroll.toString()) - //Text(mapVM.state.scale.toString()) } } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SensorsFilterChip( - name: String, - checkFilter: () -> Boolean, - updateFilter: () -> Unit, -) { - FilterChip( - selected = checkFilter(), - onClick = updateFilter, - label = { Text(name) }, - ) } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e6c67e6..037d8ed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,4 +18,6 @@ Все Термометры Камеры + Поиск + Фильтр \ No newline at end of file From 3a873725f289fdee4b0cac85dc2e71d174cf29f5 Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Tue, 6 Jun 2023 17:12:20 +0400 Subject: [PATCH 05/28] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20BottomSheet=20=D0=B4=D0=BB=D1=8F=20=D1=84=D0=B8?= =?UTF-8?q?=D0=BB=D1=8C=D1=82=D1=80=D0=BE=D0=B2=20=D0=B8=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 50 ++++++++++++++++++- app/src/main/res/values/strings.xml | 28 +++++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index d08d804..d016d78 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.AssistChip import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilterChip +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.SearchBar import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -34,6 +36,35 @@ fun SensorsPage(navController: NavController) { var searchQuery by remember { mutableStateOf("") } var searchActive by remember { mutableStateOf(false) } + var filterShown by remember { mutableStateOf(false) } + var filterMine by remember { mutableStateOf(false) } + + val filterItems = listOf( + stringResource(R.string.filter_temp), + stringResource(R.string.filter_temp_water), + stringResource(R.string.filter_temp_ground), + stringResource(R.string.filter_temp_dew_point), + stringResource(R.string.filter_humidity), + stringResource(R.string.filter_pressure), + stringResource(R.string.filter_lightness), + stringResource(R.string.filter_uv), + stringResource(R.string.filter_radiation), + stringResource(R.string.filter_rainfall), + stringResource(R.string.filter_dust), + stringResource(R.string.filter_wind_speed), + stringResource(R.string.filter_wind_direction), + stringResource(R.string.filter_concentration), + stringResource(R.string.filter_power), + stringResource(R.string.filter_voltage), + stringResource(R.string.filter_amperage), + stringResource(R.string.filter_energy), + stringResource(R.string.filter_battery), + stringResource(R.string.filter_rxtx), + stringResource(R.string.filter_signal), + stringResource(R.string.filter_water_meter), + stringResource(R.string.filter_time), + ) + val scrConfig = LocalConfiguration.current val mapHeight = scrConfig.screenHeightDp / 3 @@ -61,10 +92,27 @@ fun SensorsPage(navController: NavController) { modifier = Modifier.padding(horizontal = 8.dp), ) { AssistChip( - onClick = { }, + onClick = { filterShown = true }, label = { Text(text = stringResource(R.string.sensors_filter)) }, ) + + AssistChip( + onClick = { }, + label = { Text(text = stringResource(R.string.sensors_sorting)) }, + ) + + FilterChip( + selected = filterMine, + onClick = { filterMine = !filterMine }, + label = { Text(text = stringResource(R.string.sensors_mine)) }, + ) } } } + + if (filterShown) { + ModalBottomSheet(onDismissRequest = { filterShown = false }) { + Text(text = "Hello") + } + } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 037d8ed..5db243f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,9 +15,31 @@ Примите необходимые соглашения Сенсоры Ожидаю соглашение пользователя - Все - Термометры - Камеры Поиск Фильтр + Сортировка + Мои + Температура точки росы + Температура воздуха + Температура воды + Температура почвы + Влажность + Давление + Освещённость + УФ-индекс + Радиация + Осадки + Запылённость + Скорость ветра + Направление ветра + Концентрация + Мощность + Напряжение + Сила тока + Энергия + % батареи + Rx/Tx трафик + Сигнал в dBm + Счётчик воды + Время работы \ No newline at end of file From 2a318d551c1fa8782257a0b2846c3ac74c7f336e Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Tue, 6 Jun 2023 17:50:37 +0400 Subject: [PATCH 06/28] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D1=81=D0=BE=D0=B4=D0=B5=D1=80=D0=B6=D0=B8?= =?UTF-8?q?=D0=BC=D0=BE=D0=B5=20=D0=B2=20BottomSheet=20=D1=84=D0=B8=D0=BB?= =?UTF-8?q?=D1=8C=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 38 ++++++++++++++++--- app/src/main/res/values/strings.xml | 1 + 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index d016d78..07da696 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -1,12 +1,16 @@ 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.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.Card import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.ModalBottomSheet @@ -21,6 +25,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource 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.ui.elements.GenericNavScaffold @@ -39,7 +44,7 @@ fun SensorsPage(navController: NavController) { var filterShown by remember { mutableStateOf(false) } var filterMine by remember { mutableStateOf(false) } - val filterItems = listOf( + val filterItems = listOf( stringResource(R.string.filter_temp), stringResource(R.string.filter_temp_water), stringResource(R.string.filter_temp_ground), @@ -82,9 +87,7 @@ fun SensorsPage(navController: NavController) { onQueryChange = { query -> searchQuery = query }, onSearch = { searchActive = false }, placeholder = { Text(stringResource(R.string.search)) }, - modifier = Modifier - //.padding(horizontal = 8.dp) - .fillMaxWidth() + modifier = Modifier.fillMaxWidth() ) {} Row( @@ -112,7 +115,32 @@ fun SensorsPage(navController: NavController) { if (filterShown) { ModalBottomSheet(onDismissRequest = { filterShown = false }) { - Text(text = "Hello") + Text( + text = stringResource(R.string.sensors_filter_title), + fontSize = 26.sp, + ) + + LazyColumn( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(horizontal = 4.dp), + ) { + items(filterItems) { + Card( + modifier = Modifier + .fillMaxWidth() + .clickable { } + ) { + Text( + text = it, + fontSize = 18.sp, + modifier = Modifier.padding( + horizontal = 8.dp, + vertical = 16.dp, + ), + ) + } + } + } } } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5db243f..4661411 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,4 +42,5 @@ Сигнал в dBm Счётчик воды Время работы + Тип датчиков \ No newline at end of file From d1e7b603786ed15050eb3547b42f4125119fde19 Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Tue, 6 Jun 2023 20:56:15 +0400 Subject: [PATCH 07/28] =?UTF-8?q?=D0=A4=D0=B8=D0=BB=D1=8C=D1=82=D1=80:=20C?= =?UTF-8?q?ard-=D1=8B=20=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D1=8B?= =?UTF-8?q?=20=D0=BD=D0=B0=20CheckBox+Text,=20=D1=81=D0=BE=D0=B7=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=20=D0=B4=D0=B0=D1=82=D0=B0=D0=BA=D0=BB=D0=B0=D1=81?= =?UTF-8?q?=D1=81=20=D0=B4=D0=BB=D1=8F=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 89 ++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 07da696..fe77b98 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -10,7 +10,7 @@ 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.Card +import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.ModalBottomSheet @@ -21,7 +21,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.VerticalAlignmentLine import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -31,9 +33,7 @@ import ru.nm17.narodmon.R import ru.nm17.narodmon.ui.elements.GenericNavScaffold import ru.nm17.narodmon.ui.elements.TileMap -enum class SensorsFilter { - All, Thermometer, Camera, -} +data class SensorFilter(val stringRes: Int, val code: Int, var enabled: Boolean = false) @ExperimentalMaterial3Api @Composable @@ -44,31 +44,37 @@ fun SensorsPage(navController: NavController) { var filterShown by remember { mutableStateOf(false) } var filterMine by remember { mutableStateOf(false) } - val filterItems = listOf( - stringResource(R.string.filter_temp), - stringResource(R.string.filter_temp_water), - stringResource(R.string.filter_temp_ground), - stringResource(R.string.filter_temp_dew_point), - stringResource(R.string.filter_humidity), - stringResource(R.string.filter_pressure), - stringResource(R.string.filter_lightness), - stringResource(R.string.filter_uv), - stringResource(R.string.filter_radiation), - stringResource(R.string.filter_rainfall), - stringResource(R.string.filter_dust), - stringResource(R.string.filter_wind_speed), - stringResource(R.string.filter_wind_direction), - stringResource(R.string.filter_concentration), - stringResource(R.string.filter_power), - stringResource(R.string.filter_voltage), - stringResource(R.string.filter_amperage), - stringResource(R.string.filter_energy), - stringResource(R.string.filter_battery), - stringResource(R.string.filter_rxtx), - stringResource(R.string.filter_signal), - stringResource(R.string.filter_water_meter), - stringResource(R.string.filter_time), - ) + val filterItems = remember { + mutableListOf( + /* TODO: + * Заменить `code` на настоящее значение + * либо динамически его подгружать из ответа АПИ + * (см. /appInit, ключ в жсоне: types.type) */ + SensorFilter(R.string.filter_temp, 0), + SensorFilter(R.string.filter_temp_water, 1), + SensorFilter(R.string.filter_temp_ground, 2), + SensorFilter(R.string.filter_temp_dew_point, 3), + SensorFilter(R.string.filter_humidity, 4), + SensorFilter(R.string.filter_pressure, 5), + SensorFilter(R.string.filter_lightness, 6), + SensorFilter(R.string.filter_uv, 7), + SensorFilter(R.string.filter_radiation, 8), + SensorFilter(R.string.filter_rainfall, 9), + SensorFilter(R.string.filter_dust, 10), + SensorFilter(R.string.filter_wind_speed, 11), + SensorFilter(R.string.filter_wind_direction, 12), + SensorFilter(R.string.filter_concentration, 13), + SensorFilter(R.string.filter_power, 14), + SensorFilter(R.string.filter_voltage, 15), + SensorFilter(R.string.filter_amperage, 16), + SensorFilter(R.string.filter_energy, 17), + SensorFilter(R.string.filter_battery, 18), + SensorFilter(R.string.filter_rxtx, 19), + SensorFilter(R.string.filter_signal, 20), + SensorFilter(R.string.filter_water_meter, 21), + SensorFilter(R.string.filter_time, 22), + ) + } val scrConfig = LocalConfiguration.current val mapHeight = scrConfig.screenHeightDp / 3 @@ -125,19 +131,22 @@ fun SensorsPage(navController: NavController) { modifier = Modifier.padding(horizontal = 4.dp), ) { items(filterItems) { - Card( - modifier = Modifier - .fillMaxWidth() - .clickable { } + Row( + verticalAlignment = Alignment.CenterVertically, ) { - Text( - text = it, - fontSize = 18.sp, - modifier = Modifier.padding( - horizontal = 8.dp, - vertical = 16.dp, - ), + Checkbox( + checked = it.enabled, + onCheckedChange = { checked -> it.enabled = checked }, ) + + Text( + text = stringResource(id = it.stringRes), + modifier = Modifier.clickable { + it.enabled = !it.enabled + } + ) + + Text(text = it.toString()) } } } From 5639ebd4cda2c3f2dd0d16ba48d78f45fa337c58 Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Tue, 6 Jun 2023 21:14:50 +0400 Subject: [PATCH 08/28] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=87=D0=B5=D0=BA=D0=B1=D0=BE=D0=BA?= =?UTF-8?q?=D1=81=D1=8B=20(=D0=B7=D0=B0=D0=B1=D1=8B=D0=BB=20MutableState?= =?UTF-8?q?=20=D0=B2=20=D0=B4=D0=B0=D1=82=D0=B0=D0=BA=D0=BB=D0=B0=D1=81?= =?UTF-8?q?=D1=81=D0=B5),=20=D1=86=D0=B5=D0=BD=D1=82=D1=80=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B7=D0=B0=D0=B3=D0=BE=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=D0=BE=D0=BA=20BottomSheet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index fe77b98..1f7dbbd 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -17,15 +17,16 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.SearchBar import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.VerticalAlignmentLine import androidx.compose.ui.platform.LocalConfiguration 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 @@ -33,7 +34,11 @@ import ru.nm17.narodmon.R import ru.nm17.narodmon.ui.elements.GenericNavScaffold import ru.nm17.narodmon.ui.elements.TileMap -data class SensorFilter(val stringRes: Int, val code: Int, var enabled: Boolean = false) +data class SensorFilter( + val stringRes: Int, + val code: Int, + var enabled: MutableState = mutableStateOf(false), +) @ExperimentalMaterial3Api @Composable @@ -45,7 +50,7 @@ fun SensorsPage(navController: NavController) { var filterMine by remember { mutableStateOf(false) } val filterItems = remember { - mutableListOf( + listOf( /* TODO: * Заменить `code` на настоящее значение * либо динамически его подгружать из ответа АПИ @@ -121,13 +126,18 @@ fun SensorsPage(navController: NavController) { if (filterShown) { ModalBottomSheet(onDismissRequest = { filterShown = false }) { - Text( - text = stringResource(R.string.sensors_filter_title), - fontSize = 26.sp, - ) + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth(), + ) { + Text( + text = stringResource(R.string.sensors_filter_title), + fontSize = 24.sp, + fontWeight = FontWeight(500), + ) + } LazyColumn( - verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(horizontal = 4.dp), ) { items(filterItems) { @@ -135,18 +145,17 @@ fun SensorsPage(navController: NavController) { verticalAlignment = Alignment.CenterVertically, ) { Checkbox( - checked = it.enabled, - onCheckedChange = { checked -> it.enabled = checked }, + checked = it.enabled.value, + onCheckedChange = { checked -> + it.enabled.value = checked + }, ) - Text( text = stringResource(id = it.stringRes), modifier = Modifier.clickable { - it.enabled = !it.enabled + it.enabled.value = !it.enabled.value } ) - - Text(text = it.toString()) } } } From e6f7ff8238a53653e8d59b131433758ab7310446 Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Wed, 7 Jun 2023 15:41:44 +0400 Subject: [PATCH 09/28] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20Gradle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a952870..a1a48e8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.2.0-alpha06" +agp = "8.2.0-alpha07" kotlin = "1.8.10" core-ktx = "1.9.0" junit = "4.13.2" From ebd8dadf7ff3dbac6bec59f26f4548843de3500d Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Wed, 7 Jun 2023 15:41:53 +0400 Subject: [PATCH 10/28] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 53 ++++++++++++++++++- app/src/main/res/values/strings.xml | 5 ++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 1f7dbbd..c7d61a9 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -14,13 +14,14 @@ import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.RadioButton import androidx.compose.material3.SearchBar import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue 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 @@ -40,12 +41,35 @@ data class SensorFilter( var enabled: MutableState = mutableStateOf(false), ) +data class SensorSortingItem( + val stringRes: Int, + val sortingType: SortingType, +) + +enum class SortingType { + DISTANCE, TYPE, UPD_TIME, + NAME, VALUE, +} + @ExperimentalMaterial3Api @Composable fun SensorsPage(navController: NavController) { var searchQuery by remember { mutableStateOf("") } var searchActive by remember { mutableStateOf(false) } + var sortingShown by remember { mutableStateOf(false) } + var sortingType by remember { mutableStateOf(SortingType.DISTANCE) } + + val sortingTypes = remember { + listOf( + SensorSortingItem(R.string.sort_distance, SortingType.DISTANCE), + SensorSortingItem(R.string.sort_type, SortingType.TYPE), + SensorSortingItem(R.string.sort_update_time, SortingType.UPD_TIME), + SensorSortingItem(R.string.sort_name, SortingType.NAME), + SensorSortingItem(R.string.sort_value, SortingType.VALUE), + ) + } + var filterShown by remember { mutableStateOf(false) } var filterMine by remember { mutableStateOf(false) } @@ -111,7 +135,7 @@ fun SensorsPage(navController: NavController) { ) AssistChip( - onClick = { }, + onClick = { sortingShown = true }, label = { Text(text = stringResource(R.string.sensors_sorting)) }, ) @@ -161,4 +185,29 @@ fun SensorsPage(navController: NavController) { } } } + + if (sortingShown) { + ModalBottomSheet(onDismissRequest = { sortingShown = false }) { + LazyColumn( + modifier = Modifier.padding(horizontal = 4.dp), + ) { + items (sortingTypes) { + Row ( + verticalAlignment = Alignment.CenterVertically, + ) { + RadioButton( + selected = (sortingType == it.sortingType), + onClick = { sortingType = it.sortingType }, + ) + Text( + text = stringResource(id = it.stringRes), + modifier = Modifier.clickable { + sortingType = it.sortingType + } + ) + } + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4661411..d2ceb5e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,4 +43,9 @@ Счётчик воды Время работы Тип датчиков + Расстояние + Тип датчика + Время последнего обновления + Название + Данные \ No newline at end of file From 965533c8cc2ba8d7bc334115f02deceaacdb4e7e Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Wed, 7 Jun 2023 15:42:40 +0400 Subject: [PATCH 11/28] =?UTF-8?q?=D0=9D=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85:=20filter/sortingShown=20->=20...Show?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index c7d61a9..b5e2f81 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -57,7 +57,7 @@ fun SensorsPage(navController: NavController) { var searchQuery by remember { mutableStateOf("") } var searchActive by remember { mutableStateOf(false) } - var sortingShown by remember { mutableStateOf(false) } + var sortingShow by remember { mutableStateOf(false) } var sortingType by remember { mutableStateOf(SortingType.DISTANCE) } val sortingTypes = remember { @@ -70,7 +70,7 @@ fun SensorsPage(navController: NavController) { ) } - var filterShown by remember { mutableStateOf(false) } + var filterShow by remember { mutableStateOf(false) } var filterMine by remember { mutableStateOf(false) } val filterItems = remember { @@ -130,12 +130,12 @@ fun SensorsPage(navController: NavController) { modifier = Modifier.padding(horizontal = 8.dp), ) { AssistChip( - onClick = { filterShown = true }, + onClick = { filterShow = true }, label = { Text(text = stringResource(R.string.sensors_filter)) }, ) AssistChip( - onClick = { sortingShown = true }, + onClick = { sortingShow = true }, label = { Text(text = stringResource(R.string.sensors_sorting)) }, ) @@ -148,8 +148,8 @@ fun SensorsPage(navController: NavController) { } } - if (filterShown) { - ModalBottomSheet(onDismissRequest = { filterShown = false }) { + if (filterShow) { + ModalBottomSheet(onDismissRequest = { filterShow = false }) { Row( horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth(), @@ -186,8 +186,8 @@ fun SensorsPage(navController: NavController) { } } - if (sortingShown) { - ModalBottomSheet(onDismissRequest = { sortingShown = false }) { + if (sortingShow) { + ModalBottomSheet(onDismissRequest = { sortingShow = false }) { LazyColumn( modifier = Modifier.padding(horizontal = 4.dp), ) { From 95e849e27ae02d4ea58e1a4ccb55d60043e25aa5 Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Wed, 7 Jun 2023 16:02:42 +0400 Subject: [PATCH 12/28] =?UTF-8?q?=D0=A7=D0=B5=D0=BA=D0=B1=D0=BE=D0=BA?= =?UTF-8?q?=D1=81/=D1=80=D0=B0=D0=B4=D0=B8=D0=BE=20=D0=B2=D1=8B=D0=BD?= =?UTF-8?q?=D0=B5=D1=81=D0=B5=D0=BD=D1=8B=20=D0=B2=20=D0=BE=D1=82=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20composable,=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D0=BF?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 101 ++++++++++++------ app/src/main/res/values/strings.xml | 3 + 2 files changed, 74 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index b5e2f81..f5f1d60 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -59,6 +59,9 @@ fun SensorsPage(navController: NavController) { var sortingShow by remember { mutableStateOf(false) } var sortingType by remember { mutableStateOf(SortingType.DISTANCE) } + var sortingDesc by remember { mutableStateOf(false) } + var sortingMine by remember { mutableStateOf(false) } + var sortingFav by remember { mutableStateOf(false) } val sortingTypes = remember { listOf( @@ -165,22 +168,11 @@ fun SensorsPage(navController: NavController) { modifier = Modifier.padding(horizontal = 4.dp), ) { items(filterItems) { - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Checkbox( - checked = it.enabled.value, - onCheckedChange = { checked -> - it.enabled.value = checked - }, - ) - Text( - text = stringResource(id = it.stringRes), - modifier = Modifier.clickable { - it.enabled.value = !it.enabled.value - } - ) - } + FilterCheckbox( + checked = it.enabled.value, + onCheckedChange = { it.enabled.value = !it.enabled.value }, + stringRes = it.stringRes, + ) } } } @@ -192,22 +184,71 @@ fun SensorsPage(navController: NavController) { modifier = Modifier.padding(horizontal = 4.dp), ) { items (sortingTypes) { - Row ( - verticalAlignment = Alignment.CenterVertically, - ) { - RadioButton( - selected = (sortingType == it.sortingType), - onClick = { sortingType = it.sortingType }, - ) - Text( - text = stringResource(id = it.stringRes), - modifier = Modifier.clickable { - sortingType = it.sortingType - } - ) - } + FilterRadioButton( + selected = (sortingType == it.sortingType), + onClick = { sortingType = it.sortingType }, + stringRes = it.stringRes, + ) + } + + item { + FilterCheckbox( + checked = sortingDesc, + onCheckedChange = { sortingDesc = !sortingDesc }, + stringRes = R.string.sort_option_desc, + ) + } + + item { + FilterCheckbox( + checked = sortingFav, + onCheckedChange = { sortingFav = !sortingFav }, + stringRes = R.string.sort_option_fav, + ) + } + + item { + FilterCheckbox( + checked = sortingMine, + onCheckedChange = { sortingMine = !sortingMine }, + stringRes = R.string.sort_option_mine, + ) } } } } +} + +@ExperimentalMaterial3Api +@Composable +fun FilterCheckbox(checked: Boolean, onCheckedChange: () -> Unit, stringRes: Int) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Checkbox( + checked = checked, + onCheckedChange = { onCheckedChange() }, + ) + Text( + text = stringResource(id = stringRes), + modifier = Modifier.clickable { onCheckedChange() }, + ) + } +} + +@ExperimentalMaterial3Api +@Composable +fun FilterRadioButton(selected: Boolean, onClick: () -> Unit, stringRes: Int) { + Row ( + verticalAlignment = Alignment.CenterVertically, + ) { + RadioButton( + selected = selected, + onClick = onClick, + ) + Text( + text = stringResource(id = stringRes), + modifier = Modifier.clickable { onClick() }, + ) + } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d2ceb5e..b16dda2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,4 +48,7 @@ Время последнего обновления Название Данные + По убыванию + Избранные сверху + Мои датчики сверху \ No newline at end of file From 12db4ba8df9536754df5d9bedf0414a7b343c1dc Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Wed, 7 Jun 2023 16:07:45 +0400 Subject: [PATCH 13/28] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D0=BE=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=D0=BE=D0=BA=20=D0=B4=D0=BB=D1=8F=20BottomSheet=20=D1=81?= =?UTF-8?q?=20=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=BE?= =?UTF-8?q?=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/ru/nm17/narodmon/ui/pages/Sensors.kt | 11 +++++++++++ app/src/main/res/values/strings.xml | 1 + 2 files changed, 12 insertions(+) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index f5f1d60..9a8b6de 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -180,6 +180,17 @@ fun SensorsPage(navController: NavController) { if (sortingShow) { ModalBottomSheet(onDismissRequest = { sortingShow = false }) { + 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), ) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b16dda2..2694904 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -51,4 +51,5 @@ По убыванию Избранные сверху Мои датчики сверху + Сортировка \ No newline at end of file From 9c6be5d8c5df82c46b0b712914eae0fc86ea0eab Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Wed, 7 Jun 2023 16:45:32 +0400 Subject: [PATCH 14/28] =?UTF-8?q?=D0=9F=D0=BE=D1=84=D0=B8=D0=BA=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=BA=D1=80=D1=83=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B2=20BottomSheet-=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 9a8b6de..5b104ab 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -165,7 +165,9 @@ fun SensorsPage(navController: NavController) { } LazyColumn( - modifier = Modifier.padding(horizontal = 4.dp), + modifier = Modifier + .padding(horizontal = 4.dp) + .fillMaxWidth(), ) { items(filterItems) { FilterCheckbox( @@ -192,9 +194,11 @@ fun SensorsPage(navController: NavController) { } LazyColumn( - modifier = Modifier.padding(horizontal = 4.dp), + modifier = Modifier + .padding(horizontal = 4.dp) + .fillMaxWidth(), ) { - items (sortingTypes) { + items(sortingTypes) { FilterRadioButton( selected = (sortingType == it.sortingType), onClick = { sortingType = it.sortingType }, From 9739d1cdfe50c5320e75e1f8f65b30e907fbb7ef Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Wed, 7 Jun 2023 21:07:46 +0400 Subject: [PATCH 15/28] =?UTF-8?q?=D0=A0=D0=B0=D0=B7=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D0=BD=20UI=20=D0=B4=D0=BB=D1=8F=20=D1=81?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BA=D0=B0=20=D0=B4=D0=B0=D1=82=D1=87=D0=B8?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 5b104ab..3c2938a 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -4,18 +4,24 @@ 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.Card 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.ListItem import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.RadioButton import androidx.compose.material3.SearchBar +import androidx.compose.material3.Shapes import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState @@ -25,17 +31,39 @@ 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.graphics.Shape import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontFamily 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.elements.GenericNavScaffold import ru.nm17.narodmon.ui.elements.TileMap +data class Sensor( + // TODO: Вынести в отдельный класс, и явно не в директорию `ui` + val id: Int, + val type: SensorType, + val deviceName: String, + val deviceOwner: Int, + val name: String, + val favorite: Boolean, + val public: Boolean, + val mine: Boolean, + val location: String, + val distance: Double, // километры + val value: Double, + val unit: String, + val changed: Int, +) + data class SensorFilter( + // TODO: Можно попробовать объединить с db/SensorType.kt val stringRes: Int, val code: Int, var enabled: MutableState = mutableStateOf(false), @@ -108,6 +136,39 @@ fun SensorsPage(navController: NavController) { ) } + val sensors = remember { + mutableListOf( + // TODO: загружать датчики с сервера. Этот список -- для макета + Sensor( + 0, + SensorType(0, "temp", "C"), + "device0", 0, + "sensor0", favorite = true, + public = true, mine = false, + "Москва", 0.4, + 20.0, "C", 1686142800, + ), + Sensor( + 1, + SensorType(4, "humidity", "%"), + "device1", 0, + "sensor1", favorite = true, + public = false, mine = false, + "Подмосковье", 1.1, + 39.0, "%", 1686142800, + ), + Sensor( + 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 @@ -148,6 +209,16 @@ fun SensorsPage(navController: NavController) { label = { Text(text = stringResource(R.string.sensors_mine)) }, ) } + + Divider() + + LazyColumn( + modifier = Modifier.fillMaxHeight(), + ) { + items(sensors) { sensor -> + SensorItem(sensor) + } + } } } @@ -234,6 +305,33 @@ fun SensorsPage(navController: NavController) { } } +@ExperimentalMaterial3Api +@Composable +fun SensorItem(sensor: Sensor) { + ListItem( + overlineContent = { Text(text = "${sensor.deviceName} от ${sensor.deviceOwner}") }, + headlineContent = { Text(text = sensor.type.name) }, + supportingContent = { Text(text = sensor.name) }, + trailingContent = { + Column( + horizontalAlignment = Alignment.End, + ) { + Text(text = "${sensor.distance} km") + ElevatedCard( + shape = RectangleShape, + ) { + Text( + text = "${sensor.value} ${sensor.unit}", + fontFamily = FontFamily.Monospace, + fontSize = 14.sp, + modifier = Modifier.padding(horizontal = 3.dp, vertical = 1.dp) + ) + } + } + } + ) +} + @ExperimentalMaterial3Api @Composable fun FilterCheckbox(checked: Boolean, onCheckedChange: () -> Unit, stringRes: Int) { From 4c3584d63c864a42e174cb5dcc9aaf975b2f81bf Mon Sep 17 00:00:00 2001 From: DarkCat09 Date: Wed, 7 Jun 2023 21:42:26 +0400 Subject: [PATCH 16/28] =?UTF-8?q?=D0=A8=D1=80=D0=B8=D1=84=D1=82=20Iosevka?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=B4=D0=B0=D1=82=D1=87=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/ru/nm17/narodmon/ui/Font.kt | 10 ++++++++++ .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 4 +++- app/src/main/res/font/iosevka.ttf | Bin 0 -> 75308 bytes 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/Font.kt create mode 100644 app/src/main/res/font/iosevka.ttf diff --git a/app/src/main/java/ru/nm17/narodmon/ui/Font.kt b/app/src/main/java/ru/nm17/narodmon/ui/Font.kt new file mode 100644 index 0000000..a1c20f0 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/Font.kt @@ -0,0 +1,10 @@ +package ru.nm17.narodmon.ui + +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import ru.nm17.narodmon.R + +val iosevkaFamily = FontFamily( + Font(R.font.iosevka, FontWeight.Medium) +) \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 3c2938a..93d5117 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -44,6 +44,7 @@ import ru.nm17.narodmon.R import ru.nm17.narodmon.db.entities.SensorType import ru.nm17.narodmon.ui.elements.GenericNavScaffold import ru.nm17.narodmon.ui.elements.TileMap +import ru.nm17.narodmon.ui.iosevkaFamily data class Sensor( // TODO: Вынести в отдельный класс, и явно не в директорию `ui` @@ -322,7 +323,8 @@ fun SensorItem(sensor: Sensor) { ) { Text( text = "${sensor.value} ${sensor.unit}", - fontFamily = FontFamily.Monospace, + fontFamily = iosevkaFamily, + fontWeight = FontWeight.Medium, fontSize = 14.sp, modifier = Modifier.padding(horizontal = 3.dp, vertical = 1.dp) ) diff --git a/app/src/main/res/font/iosevka.ttf b/app/src/main/res/font/iosevka.ttf new file mode 100644 index 0000000000000000000000000000000000000000..bab9e518b5e5eb8eb659bf5159a31aba78a9a99b GIT binary patch literal 75308 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 literal 0 HcmV?d00001 From be9b491c44fcb7dd1ec9e0adc2bb4ab7df4faaa5 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 17:14:04 +0500 Subject: [PATCH 17/28] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BB=20=D0=BB?= =?UTF-8?q?=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8?= =?UTF-8?q?=D0=BE=D1=82=D0=B5=D0=BA=D0=B8.=20=D0=9C=D1=8B=20=D0=B6=D0=B5?= =?UTF-8?q?=20=D0=BD=D0=B5=20=D0=B1=D1=83=D0=B4=D0=B5=D0=BC=20RxJava=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9a73f7f..9be7f7e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -106,12 +106,6 @@ dependencies { // optional - Kotlin Extensions and Coroutines support for Room implementation("androidx.room:room-ktx:$room_version") - // optional - RxJava2 support for Room - implementation("androidx.room:room-rxjava2:$room_version") - - // optional - RxJava3 support for Room - implementation("androidx.room:room-rxjava3:$room_version") - // optional - Guava support for Room, including Optional and ListenableFuture implementation("androidx.room:room-guava:$room_version") From b17e8531036f5c4b4cb0ee512a57577c170a7533 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 17:15:35 +0500 Subject: [PATCH 18/28] =?UTF-8?q?=D0=A7=D0=B5=D0=BA=D0=B1=D0=BE=D0=BA?= =?UTF-8?q?=D1=81=D1=8B=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=BA=D0=BB=D0=B8=D0=BA=D0=B8=D0=B2=D0=B0?= =?UTF-8?q?=D1=8E=D1=82=D1=81=D1=8F=20=D0=BF=D0=BE=20=D0=B2=D1=81=D0=B5?= =?UTF-8?q?=D0=B9=20=D1=88=D0=B8=D1=80=D0=B8=D0=BD=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 93d5117..eb9fd98 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -11,7 +11,6 @@ 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.Card import androidx.compose.material3.Checkbox import androidx.compose.material3.Divider import androidx.compose.material3.ElevatedCard @@ -21,7 +20,6 @@ import androidx.compose.material3.ListItem import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.RadioButton import androidx.compose.material3.SearchBar -import androidx.compose.material3.Shapes import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState @@ -32,10 +30,8 @@ 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.graphics.Shape import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -244,9 +240,8 @@ fun SensorsPage(navController: NavController) { items(filterItems) { FilterCheckbox( checked = it.enabled.value, - onCheckedChange = { it.enabled.value = !it.enabled.value }, stringRes = it.stringRes, - ) + ) { it.enabled.value = !it.enabled.value } } } } @@ -264,7 +259,7 @@ fun SensorsPage(navController: NavController) { fontWeight = FontWeight(500), ) } - + LazyColumn( modifier = Modifier .padding(horizontal = 4.dp) @@ -281,25 +276,22 @@ fun SensorsPage(navController: NavController) { item { FilterCheckbox( checked = sortingDesc, - onCheckedChange = { sortingDesc = !sortingDesc }, stringRes = R.string.sort_option_desc, - ) + ) { sortingDesc = !sortingDesc } } item { FilterCheckbox( checked = sortingFav, - onCheckedChange = { sortingFav = !sortingFav }, stringRes = R.string.sort_option_fav, - ) + ) { sortingFav = !sortingFav } } item { FilterCheckbox( checked = sortingMine, - onCheckedChange = { sortingMine = !sortingMine }, stringRes = R.string.sort_option_mine, - ) + ) { sortingMine = !sortingMine } } } } @@ -336,9 +328,10 @@ fun SensorItem(sensor: Sensor) { @ExperimentalMaterial3Api @Composable -fun FilterCheckbox(checked: Boolean, onCheckedChange: () -> Unit, stringRes: Int) { +fun FilterCheckbox(checked: Boolean, stringRes: Int, onCheckedChange: () -> Unit) { Row( verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth().clickable { onCheckedChange() } ) { Checkbox( checked = checked, @@ -346,7 +339,6 @@ fun FilterCheckbox(checked: Boolean, onCheckedChange: () -> Unit, stringRes: Int ) Text( text = stringResource(id = stringRes), - modifier = Modifier.clickable { onCheckedChange() }, ) } } From c32ea30772aacb597cb063f47b87dcd79488052c Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 17:16:30 +0500 Subject: [PATCH 19/28] =?UTF-8?q?=D0=9E=D1=82=D1=81=D1=82=D1=83=D0=BF?= =?UTF-8?q?=D1=8B=20=20=D1=83=20searchbar'=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index eb9fd98..566f394 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -183,7 +183,7 @@ fun SensorsPage(navController: NavController) { onQueryChange = { query -> searchQuery = query }, onSearch = { searchActive = false }, placeholder = { Text(stringResource(R.string.search)) }, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth().padding(horizontal = if(!searchActive) 8.dp else 0.dp) ) {} Row( From 9d024a0c6966a78fd70f75e14c2b71e1859ff8fd Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 18:32:24 +0500 Subject: [PATCH 20/28] =?UTF-8?q?Bottom=20Sheet'=D1=8B=20=D1=82=D0=B5?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B6=D0=B8=D0=B2=D1=83=D1=82=20?= =?UTF-8?q?=D0=B2=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D1=85?= =?UTF-8?q?=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0=D1=85,=20=D1=87=D1=82=D0=BE?= =?UTF-8?q?=D0=B1=D1=8B=20Sensors.kt=20=D0=B1=D1=8B=D0=BB=20=D1=87=D0=B8?= =?UTF-8?q?=D1=82=D0=B0=D0=B1=D0=B5=D0=BB=D1=8C=D0=BD=D0=B5=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bottomSheets/FilterSensorsBottomSheet.kt | 53 ++++++++++ .../ui/bottomSheets/SortSensorsBottomSheet.kt | 97 +++++++++++++++++++ .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 82 ++-------------- 3 files changed, 160 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/SortSensorsBottomSheet.kt diff --git a/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt b/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt new file mode 100644 index 0000000..92283bd --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt @@ -0,0 +1,53 @@ +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.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +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 ru.nm17.narodmon.R +import ru.nm17.narodmon.ui.pages.FilterCheckbox +import ru.nm17.narodmon.ui.pages.SensorFilter + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun FilterSensorsBottomSheet( + filterItems: List, + onDismissRequest: () -> Unit +) { + ModalBottomSheet(onDismissRequest = { onDismissRequest.invoke() }) { + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth(), + ) { + Text( + text = stringResource(R.string.sensors_filter_title), + fontSize = 24.sp, + fontWeight = FontWeight(500), + ) + } + + LazyColumn( + modifier = Modifier + .padding(horizontal = 4.dp) + .fillMaxWidth(), + ) { + items(filterItems) { + FilterCheckbox( + checked = it.enabled.value, + stringRes = it.stringRes, + ) { it.enabled.value = !it.enabled.value } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/SortSensorsBottomSheet.kt b/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/SortSensorsBottomSheet.kt new file mode 100644 index 0000000..8287226 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/SortSensorsBottomSheet.kt @@ -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 = {}) {} + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 566f394..aaac7dc 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -220,81 +220,19 @@ fun SensorsPage(navController: NavController) { } if (filterShow) { - ModalBottomSheet(onDismissRequest = { filterShow = false }) { - Row( - horizontalArrangement = Arrangement.Center, - modifier = Modifier.fillMaxWidth(), - ) { - Text( - text = stringResource(R.string.sensors_filter_title), - fontSize = 24.sp, - fontWeight = FontWeight(500), - ) - } - - LazyColumn( - modifier = Modifier - .padding(horizontal = 4.dp) - .fillMaxWidth(), - ) { - items(filterItems) { - FilterCheckbox( - checked = it.enabled.value, - stringRes = it.stringRes, - ) { it.enabled.value = !it.enabled.value } - } - } - } + FilterSensorsBottomSheet( + filterItems, + onDismissRequest = { filterShow = false } + ) } if (sortingShow) { - ModalBottomSheet(onDismissRequest = { sortingShow = false }) { - 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, - ) - } - - item { - FilterCheckbox( - checked = sortingDesc, - stringRes = R.string.sort_option_desc, - ) { sortingDesc = !sortingDesc } - } - - item { - FilterCheckbox( - checked = sortingFav, - stringRes = R.string.sort_option_fav, - ) { sortingFav = !sortingFav } - } - - item { - FilterCheckbox( - checked = sortingMine, - stringRes = R.string.sort_option_mine, - ) { sortingMine = !sortingMine } - } - } - } + SortSensorsBottomSheet( + onApply = { s -> + sortingType = s + sortingShow = false + }, + onDismissRequest = { sortingShow = false }) } } From d82a4b28b7b9797f8da4ba76509037b13b29d352 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 18:33:57 +0500 Subject: [PATCH 21/28] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=B5=D1=81=20data=20?= =?UTF-8?q?=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D1=8B=20=D0=B2=20=D0=BE=D1=82?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20=D1=84=D0=B0=D0=B9?= =?UTF-8?q?=D0=BB=D1=8B.=20=D0=A2=D0=B0=D0=BA=20=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=8F=D1=82=D0=BE=20=D0=B4=D0=B5=D0=BB=D0=B0=D1=82=D1=8C.=20?= =?UTF-8?q?=D0=9F=D0=BE=20=D0=BA=D1=80=D0=B0=D0=B9=D0=BD=D0=B5=D0=B9=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=80=D0=B5=20=D1=83=20=D1=81=D0=B5=D0=B1=D1=8F=20?= =?UTF-8?q?=D1=8F=20=D1=82=D0=B0=D0=BA=20=D1=81=D1=82=D0=B0=D1=80=D0=B0?= =?UTF-8?q?=D1=8E=D1=81=D1=8C=20=D0=B4=D0=B5=D0=BB=D0=B0=D1=82=D1=8C=20:)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nm17/narodmon/ui/entities/SensorEntity.kt | 21 +++ .../ui/entities/SensorFilerUiEntity.kt | 11 ++ .../ui/entities/SensorSortingUiEntity.kt | 7 + .../ui/entities/SortingSensorsEntity.kt | 1 + .../narodmon/ui/entities/SortingTypesEnum.kt | 13 ++ .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 136 +++++++----------- 6 files changed, 103 insertions(+), 86 deletions(-) create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/entities/SensorEntity.kt create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/entities/SensorFilerUiEntity.kt create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/entities/SensorSortingUiEntity.kt create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/entities/SortingSensorsEntity.kt create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/entities/SortingTypesEnum.kt diff --git a/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorEntity.kt b/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorEntity.kt new file mode 100644 index 0000000..84aeb23 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorEntity.kt @@ -0,0 +1,21 @@ +package ru.nm17.narodmon.ui.entities + +import ru.nm17.narodmon.db.entities.SensorType + + +data class SensorEntity( + // TODO: Вынести в отдельный класс, и явно не в директорию `ui` + val id: Int, + val type: SensorType, + val deviceName: String, + val deviceOwner: Int, + val name: String, + val favorite: Boolean, + val public: Boolean, + val mine: Boolean, + val location: String, + val distance: Double, // километры + val value: Double, + val unit: String, + val changed: Int, +) \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorFilerUiEntity.kt b/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorFilerUiEntity.kt new file mode 100644 index 0000000..62277d8 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorFilerUiEntity.kt @@ -0,0 +1,11 @@ +package ru.nm17.narodmon.ui.entities + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf + +data class SensorFilterUiEntity( + // TODO: Можно попробовать объединить с db/SensorType.kt + val stringRes: Int, + val code: Int, + var enabled: MutableState = mutableStateOf(false), +) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorSortingUiEntity.kt b/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorSortingUiEntity.kt new file mode 100644 index 0000000..96e4604 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorSortingUiEntity.kt @@ -0,0 +1,7 @@ +package ru.nm17.narodmon.ui.entities + + +data class SensorSortingUiEntity( + val stringRes: Int, + val sortingType: SortingTypes, +) \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/entities/SortingSensorsEntity.kt b/app/src/main/java/ru/nm17/narodmon/ui/entities/SortingSensorsEntity.kt new file mode 100644 index 0000000..82e230c --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/entities/SortingSensorsEntity.kt @@ -0,0 +1 @@ +package ru.nm17.narodmon.ui.entities diff --git a/app/src/main/java/ru/nm17/narodmon/ui/entities/SortingTypesEnum.kt b/app/src/main/java/ru/nm17/narodmon/ui/entities/SortingTypesEnum.kt new file mode 100644 index 0000000..0cb7aa3 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/entities/SortingTypesEnum.kt @@ -0,0 +1,13 @@ +package ru.nm17.narodmon.ui.entities + +enum class SortingTypes { + DISTANCE, + DISTANCE_DESC, + TYPE, + TYPE_DESC, + UPD_TIME, + NAME, + NAME_DESC, + VALUE, + VALUE_DESC +} \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index aaac7dc..a1092f5 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -16,13 +16,12 @@ 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.ModalBottomSheet import androidx.compose.material3.RadioButton import androidx.compose.material3.SearchBar import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.getValue @@ -31,6 +30,7 @@ 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 @@ -38,43 +38,15 @@ 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 -data class Sensor( - // TODO: Вынести в отдельный класс, и явно не в директорию `ui` - val id: Int, - val type: SensorType, - val deviceName: String, - val deviceOwner: Int, - val name: String, - val favorite: Boolean, - val public: Boolean, - val mine: Boolean, - val location: String, - val distance: Double, // километры - val value: Double, - val unit: String, - val changed: Int, -) - -data class SensorFilter( - // TODO: Можно попробовать объединить с db/SensorType.kt - val stringRes: Int, - val code: Int, - var enabled: MutableState = mutableStateOf(false), -) - -data class SensorSortingItem( - val stringRes: Int, - val sortingType: SortingType, -) - -enum class SortingType { - DISTANCE, TYPE, UPD_TIME, - NAME, VALUE, -} @ExperimentalMaterial3Api @Composable @@ -82,21 +54,9 @@ 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(SortingType.DISTANCE) } - var sortingDesc by remember { mutableStateOf(false) } - var sortingMine by remember { mutableStateOf(false) } - var sortingFav by remember { mutableStateOf(false) } - val sortingTypes = remember { - listOf( - SensorSortingItem(R.string.sort_distance, SortingType.DISTANCE), - SensorSortingItem(R.string.sort_type, SortingType.TYPE), - SensorSortingItem(R.string.sort_update_time, SortingType.UPD_TIME), - SensorSortingItem(R.string.sort_name, SortingType.NAME), - SensorSortingItem(R.string.sort_value, SortingType.VALUE), - ) - } + 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) } @@ -107,36 +67,36 @@ fun SensorsPage(navController: NavController) { * Заменить `code` на настоящее значение * либо динамически его подгружать из ответа АПИ * (см. /appInit, ключ в жсоне: types.type) */ - SensorFilter(R.string.filter_temp, 0), - SensorFilter(R.string.filter_temp_water, 1), - SensorFilter(R.string.filter_temp_ground, 2), - SensorFilter(R.string.filter_temp_dew_point, 3), - SensorFilter(R.string.filter_humidity, 4), - SensorFilter(R.string.filter_pressure, 5), - SensorFilter(R.string.filter_lightness, 6), - SensorFilter(R.string.filter_uv, 7), - SensorFilter(R.string.filter_radiation, 8), - SensorFilter(R.string.filter_rainfall, 9), - SensorFilter(R.string.filter_dust, 10), - SensorFilter(R.string.filter_wind_speed, 11), - SensorFilter(R.string.filter_wind_direction, 12), - SensorFilter(R.string.filter_concentration, 13), - SensorFilter(R.string.filter_power, 14), - SensorFilter(R.string.filter_voltage, 15), - SensorFilter(R.string.filter_amperage, 16), - SensorFilter(R.string.filter_energy, 17), - SensorFilter(R.string.filter_battery, 18), - SensorFilter(R.string.filter_rxtx, 19), - SensorFilter(R.string.filter_signal, 20), - SensorFilter(R.string.filter_water_meter, 21), - SensorFilter(R.string.filter_time, 22), + SensorFilterUiEntity(R.string.filter_temp, 0), + SensorFilterUiEntity(R.string.filter_temp_water, 1), + SensorFilterUiEntity(R.string.filter_temp_ground, 2), + SensorFilterUiEntity(R.string.filter_temp_dew_point, 3), + SensorFilterUiEntity(R.string.filter_humidity, 4), + SensorFilterUiEntity(R.string.filter_pressure, 5), + SensorFilterUiEntity(R.string.filter_lightness, 6), + SensorFilterUiEntity(R.string.filter_uv, 7), + SensorFilterUiEntity(R.string.filter_radiation, 8), + SensorFilterUiEntity(R.string.filter_rainfall, 9), + SensorFilterUiEntity(R.string.filter_dust, 10), + SensorFilterUiEntity(R.string.filter_wind_speed, 11), + SensorFilterUiEntity(R.string.filter_wind_direction, 12), + SensorFilterUiEntity(R.string.filter_concentration, 13), + SensorFilterUiEntity(R.string.filter_power, 14), + SensorFilterUiEntity(R.string.filter_voltage, 15), + SensorFilterUiEntity(R.string.filter_amperage, 16), + SensorFilterUiEntity(R.string.filter_energy, 17), + SensorFilterUiEntity(R.string.filter_battery, 18), + SensorFilterUiEntity(R.string.filter_rxtx, 19), + SensorFilterUiEntity(R.string.filter_signal, 20), + SensorFilterUiEntity(R.string.filter_water_meter, 21), + SensorFilterUiEntity(R.string.filter_time, 22), ) } - val sensors = remember { + val sensorEntities = remember { mutableListOf( // TODO: загружать датчики с сервера. Этот список -- для макета - Sensor( + SensorEntity( 0, SensorType(0, "temp", "C"), "device0", 0, @@ -145,7 +105,7 @@ fun SensorsPage(navController: NavController) { "Москва", 0.4, 20.0, "C", 1686142800, ), - Sensor( + SensorEntity( 1, SensorType(4, "humidity", "%"), "device1", 0, @@ -154,7 +114,7 @@ fun SensorsPage(navController: NavController) { "Подмосковье", 1.1, 39.0, "%", 1686142800, ), - Sensor( + SensorEntity( 2, SensorType(11, "wind speed", "m/s"), "device2", 1, @@ -183,7 +143,9 @@ fun SensorsPage(navController: NavController) { 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) + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = if (!searchActive) 8.dp else 0.dp) ) {} Row( @@ -212,7 +174,7 @@ fun SensorsPage(navController: NavController) { LazyColumn( modifier = Modifier.fillMaxHeight(), ) { - items(sensors) { sensor -> + items(sensorEntities) { sensor -> SensorItem(sensor) } } @@ -238,21 +200,21 @@ fun SensorsPage(navController: NavController) { @ExperimentalMaterial3Api @Composable -fun SensorItem(sensor: Sensor) { +fun SensorItem(sensorEntity: SensorEntity) { ListItem( - overlineContent = { Text(text = "${sensor.deviceName} от ${sensor.deviceOwner}") }, - headlineContent = { Text(text = sensor.type.name) }, - supportingContent = { Text(text = sensor.name) }, + 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 = "${sensor.distance} km") + Text(text = "${sensorEntity.distance} km") ElevatedCard( shape = RectangleShape, ) { Text( - text = "${sensor.value} ${sensor.unit}", + text = "${sensorEntity.value} ${sensorEntity.unit}", fontFamily = iosevkaFamily, fontWeight = FontWeight.Medium, fontSize = 14.sp, @@ -269,7 +231,9 @@ fun SensorItem(sensor: Sensor) { fun FilterCheckbox(checked: Boolean, stringRes: Int, onCheckedChange: () -> Unit) { Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth().clickable { onCheckedChange() } + modifier = Modifier + .fillMaxWidth() + .clickable { onCheckedChange() } ) { Checkbox( checked = checked, @@ -284,7 +248,7 @@ fun FilterCheckbox(checked: Boolean, stringRes: Int, onCheckedChange: () -> Unit @ExperimentalMaterial3Api @Composable fun FilterRadioButton(selected: Boolean, onClick: () -> Unit, stringRes: Int) { - Row ( + Row( verticalAlignment = Alignment.CenterVertically, ) { RadioButton( From 01b362ce4ef6cd4e65fe5d6bd32005949df839f7 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 18:34:41 +0500 Subject: [PATCH 22/28] =?UTF-8?q?=D0=9F=D0=BE=D0=BC=D0=B5=D0=BD=D1=8F?= =?UTF-8?q?=D0=BB=20strings.xml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/values/strings.xml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2694904..21124ef 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,6 @@ Народный Мониторинг - Hello blank fragment Я принимаю соглашения Выйти @@ -18,7 +17,7 @@ Поиск Фильтр Сортировка - Мои + Мои датчики Температура точки росы Температура воздуха Температура воды @@ -43,13 +42,13 @@ Счётчик воды Время работы Тип датчиков - Расстояние - Тип датчика - Время последнего обновления - Название - Данные - По убыванию - Избранные сверху - Мои датчики сверху - Сортировка + От ближних к дальним + По типу (от А до Я) + По типу (от Я до А) + По названию (от А до Я) + По названию (от Я до А) + Сортировка датчиков + По времени обновления + Применить + От дальних к ближним \ No newline at end of file From 937f4890e16926173403bee0930e7a1a3136fd0d Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 18:48:34 +0500 Subject: [PATCH 23/28] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B8=D0=BA=D0=BE=D0=BD=D0=BA=D0=B8=20=D0=BA=20=D1=87?= =?UTF-8?q?=D0=B8=D0=BF=D0=B0=D0=BC=20=D0=B2=20Sensors.kt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/ru/nm17/narodmon/ui/pages/Sensors.kt | 12 ++++++++++++ app/src/main/res/drawable/ic_filter.xml | 5 +++++ app/src/main/res/drawable/ic_sort.xml | 5 +++++ 3 files changed, 22 insertions(+) create mode 100644 app/src/main/res/drawable/ic_filter.xml create mode 100644 app/src/main/res/drawable/ic_sort.xml diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index a1092f5..8f0e218 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -154,11 +154,23 @@ fun SensorsPage(navController: NavController) { ) { 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)) }, ) diff --git a/app/src/main/res/drawable/ic_filter.xml b/app/src/main/res/drawable/ic_filter.xml new file mode 100644 index 0000000..d574422 --- /dev/null +++ b/app/src/main/res/drawable/ic_filter.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_sort.xml b/app/src/main/res/drawable/ic_sort.xml new file mode 100644 index 0000000..ed8927f --- /dev/null +++ b/app/src/main/res/drawable/ic_sort.xml @@ -0,0 +1,5 @@ + + + From 1c8f993dcbcf7bec9f34e8203783efd5f289d475 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 18:48:47 +0500 Subject: [PATCH 24/28] =?UTF-8?q?=D0=A3=D0=BF=D1=81.=20=D0=9E=D1=87=D0=B5?= =?UTF-8?q?=D0=BF=D1=8F=D1=82=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt | 4 ++-- .../{SensorFilerUiEntity.kt => SensorFilterUiEntity.kt} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename app/src/main/java/ru/nm17/narodmon/ui/entities/{SensorFilerUiEntity.kt => SensorFilterUiEntity.kt} (100%) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt b/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt index 92283bd..63f8b6c 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt @@ -16,13 +16,13 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import ru.nm17.narodmon.R +import ru.nm17.narodmon.ui.entities.SensorFilterUiEntity import ru.nm17.narodmon.ui.pages.FilterCheckbox -import ru.nm17.narodmon.ui.pages.SensorFilter @OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterSensorsBottomSheet( - filterItems: List, + filterItems: List, onDismissRequest: () -> Unit ) { ModalBottomSheet(onDismissRequest = { onDismissRequest.invoke() }) { diff --git a/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorFilerUiEntity.kt b/app/src/main/java/ru/nm17/narodmon/ui/entities/SensorFilterUiEntity.kt similarity index 100% rename from app/src/main/java/ru/nm17/narodmon/ui/entities/SensorFilerUiEntity.kt rename to app/src/main/java/ru/nm17/narodmon/ui/entities/SensorFilterUiEntity.kt From 45c9101f9dd47a9e5601ed7edb25ab7f4e7c22fc Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 18:51:09 +0500 Subject: [PATCH 25/28] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D0=BB=20=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=83=D0=B6=D0=BD=D0=BE=D0=B5=20=D0=B8=D0=B7=20Mai?= =?UTF-8?q?nActivity.kt=20+=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D1=83=D0=B6=D0=BD=D1=8B=D0=B9=20.xml=20?= =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/ru/nm17/narodmon/MainActivity.kt | 16 ---------------- app/src/main/res/layout/fragment_sensors.xml | 14 -------------- 2 files changed, 30 deletions(-) delete mode 100644 app/src/main/res/layout/fragment_sensors.xml diff --git a/app/src/main/java/ru/nm17/narodmon/MainActivity.kt b/app/src/main/java/ru/nm17/narodmon/MainActivity.kt index b673692..baff5ae 100644 --- a/app/src/main/java/ru/nm17/narodmon/MainActivity.kt +++ b/app/src/main/java/ru/nm17/narodmon/MainActivity.kt @@ -132,23 +132,7 @@ class MainActivity : ComponentActivity() { } } -@Composable -fun Greeting(name: String, modifier: Modifier = Modifier) { - Text( - text = "Hello $name!", - modifier = modifier - ) -} - @Composable fun NavHolderEl() { //NavHost(navController = NavHostController(N), graph =) } - -@Preview(showBackground = true) -@Composable -fun GreetingPreview() { - NarodMonTheme { - Greeting("Android") - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_sensors.xml b/app/src/main/res/layout/fragment_sensors.xml deleted file mode 100644 index e076d3b..0000000 --- a/app/src/main/res/layout/fragment_sensors.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file From 9cd0bcddc87093e3cf826eaa75c46181688c051d Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 18:54:41 +0500 Subject: [PATCH 26/28] =?UTF-8?q?=D0=A0=D0=B0=D0=B4=D0=B8=D0=BE=20=D0=BA?= =?UTF-8?q?=D0=BD=D0=BE=D0=BF=D0=BA=D0=B8=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D0=BF=D1=80=D0=BE=D0=BA=D0=BB=D0=B8=D0=BA=D0=B8=D0=B2?= =?UTF-8?q?=D0=B0=D1=8E=D1=82=D1=81=D1=8F=20=D0=BF=D0=BE=20=D0=B2=D1=81?= =?UTF-8?q?=D0=B5=D0=B9=20=D1=88=D0=B8=D1=80=D0=B8=D0=BD=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 8f0e218..1abff3a 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -262,14 +262,12 @@ fun FilterCheckbox(checked: Boolean, stringRes: Int, onCheckedChange: () -> Unit 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), - modifier = Modifier.clickable { onClick() }, - ) + Text(text = stringResource(id = stringRes)) } } \ No newline at end of file From 2a6bfb205c34fb5e7892019311484168f9af0167 Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Thu, 8 Jun 2023 19:26:56 +0500 Subject: [PATCH 27/28] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=20UX=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bottomSheets/FilterSensorsBottomSheet.kt | 92 ++++++++++++++++--- .../java/ru/nm17/narodmon/ui/pages/Sensors.kt | 40 ++------ 2 files changed, 86 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt b/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt index 63f8b6c..ce8be73 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/bottomSheets/FilterSensorsBottomSheet.kt @@ -1,53 +1,119 @@ package ru.nm17.narodmon.ui.bottomSheets 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.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.SheetValue import androidx.compose.material3.Text 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.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.SensorFilterUiEntity import ru.nm17.narodmon.ui.pages.FilterCheckbox +import ru.nm17.narodmon.ui.theme.NarodMonTheme @OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterSensorsBottomSheet( - filterItems: List, + onApply: (filters: List) -> Unit, onDismissRequest: () -> Unit ) { - ModalBottomSheet(onDismissRequest = { onDismissRequest.invoke() }) { + val filterItems = remember { + listOf( + /* TODO: + * Заменить `code` на настоящее значение + * либо динамически его подгружать из ответа АПИ + * (см. /appInit, ключ в жсоне: types.type) */ + SensorFilterUiEntity(R.string.filter_temp, 0), + SensorFilterUiEntity(R.string.filter_temp_water, 1), + SensorFilterUiEntity(R.string.filter_temp_ground, 2), + SensorFilterUiEntity(R.string.filter_temp_dew_point, 3), + SensorFilterUiEntity(R.string.filter_humidity, 4), + SensorFilterUiEntity(R.string.filter_pressure, 5), + SensorFilterUiEntity(R.string.filter_lightness, 6), + SensorFilterUiEntity(R.string.filter_uv, 7), + SensorFilterUiEntity(R.string.filter_radiation, 8), + SensorFilterUiEntity(R.string.filter_rainfall, 9), + SensorFilterUiEntity(R.string.filter_dust, 10), + SensorFilterUiEntity(R.string.filter_wind_speed, 11), + SensorFilterUiEntity(R.string.filter_wind_direction, 12), + SensorFilterUiEntity(R.string.filter_concentration, 13), + SensorFilterUiEntity(R.string.filter_power, 14), + SensorFilterUiEntity(R.string.filter_voltage, 15), + SensorFilterUiEntity(R.string.filter_amperage, 16), + SensorFilterUiEntity(R.string.filter_energy, 17), + SensorFilterUiEntity(R.string.filter_battery, 18), + SensorFilterUiEntity(R.string.filter_rxtx, 19), + SensorFilterUiEntity(R.string.filter_signal, 20), + SensorFilterUiEntity(R.string.filter_water_meter, 21), + SensorFilterUiEntity(R.string.filter_time, 22), + ) + } + + 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), ) } - - LazyColumn( - modifier = Modifier - .padding(horizontal = 4.dp) - .fillMaxWidth(), - ) { - items(filterItems) { - FilterCheckbox( - checked = it.enabled.value, - stringRes = it.stringRes, - ) { it.enabled.value = !it.enabled.value } + 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 } + } } + 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() { + NarodMonTheme { + FilterSensorsBottomSheet({}) { + } } } \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt index 1abff3a..b67adfc 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/pages/Sensors.kt @@ -61,37 +61,6 @@ fun SensorsPage(navController: NavController) { var filterShow by remember { mutableStateOf(false) } var filterMine by remember { mutableStateOf(false) } - val filterItems = remember { - listOf( - /* TODO: - * Заменить `code` на настоящее значение - * либо динамически его подгружать из ответа АПИ - * (см. /appInit, ключ в жсоне: types.type) */ - SensorFilterUiEntity(R.string.filter_temp, 0), - SensorFilterUiEntity(R.string.filter_temp_water, 1), - SensorFilterUiEntity(R.string.filter_temp_ground, 2), - SensorFilterUiEntity(R.string.filter_temp_dew_point, 3), - SensorFilterUiEntity(R.string.filter_humidity, 4), - SensorFilterUiEntity(R.string.filter_pressure, 5), - SensorFilterUiEntity(R.string.filter_lightness, 6), - SensorFilterUiEntity(R.string.filter_uv, 7), - SensorFilterUiEntity(R.string.filter_radiation, 8), - SensorFilterUiEntity(R.string.filter_rainfall, 9), - SensorFilterUiEntity(R.string.filter_dust, 10), - SensorFilterUiEntity(R.string.filter_wind_speed, 11), - SensorFilterUiEntity(R.string.filter_wind_direction, 12), - SensorFilterUiEntity(R.string.filter_concentration, 13), - SensorFilterUiEntity(R.string.filter_power, 14), - SensorFilterUiEntity(R.string.filter_voltage, 15), - SensorFilterUiEntity(R.string.filter_amperage, 16), - SensorFilterUiEntity(R.string.filter_energy, 17), - SensorFilterUiEntity(R.string.filter_battery, 18), - SensorFilterUiEntity(R.string.filter_rxtx, 19), - SensorFilterUiEntity(R.string.filter_signal, 20), - SensorFilterUiEntity(R.string.filter_water_meter, 21), - SensorFilterUiEntity(R.string.filter_time, 22), - ) - } val sensorEntities = remember { mutableListOf( @@ -195,7 +164,10 @@ fun SensorsPage(navController: NavController) { if (filterShow) { FilterSensorsBottomSheet( - filterItems, + onApply = { + // TODO применение фильтров + filterShow = false + }, onDismissRequest = { filterShow = false } ) } @@ -262,7 +234,9 @@ fun FilterCheckbox(checked: Boolean, stringRes: Int, onCheckedChange: () -> Unit fun FilterRadioButton(selected: Boolean, onClick: () -> Unit, stringRes: Int) { Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth().clickable { onClick.invoke() } + modifier = Modifier + .fillMaxWidth() + .clickable { onClick.invoke() } ) { RadioButton( selected = selected, From 0bdf64b7ed0d99eb549625f6009c8f813c4d9d4f Mon Sep 17 00:00:00 2001 From: mezhendosina Date: Fri, 9 Jun 2023 19:31:08 +0500 Subject: [PATCH 28/28] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20Ui=20=D0=B4=D0=BB=D1=8F=20=D0=B2=D0=B5=D0=B1-?= =?UTF-8?q?=D0=BA=D0=B0=D0=BC=D0=B5=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 6 +- .../narodmon/ui/webCamsScreen/WebCamItem.kt | 74 +++++++++++++++++++ .../ui/webCamsScreen/WebCamUiEntity.kt | 13 ++++ .../ui/webCamsScreen/WebCamsScreen.kt | 63 ++++++++++++++++ 4 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamItem.kt create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamUiEntity.kt create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamsScreen.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9be7f7e..153e5da 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -145,4 +145,8 @@ dependencies { // Map Compose library implementation("ovh.plrapps:mapcompose:2.7.1") -} \ No newline at end of file + + // Glide + implementation ("com.github.bumptech.glide:glide:4.14.2") + implementation("com.github.bumptech.glide:compose:1.0.0-alpha.1") + } \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamItem.kt b/app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamItem.kt new file mode 100644 index 0000000..1bf1928 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamItem.kt @@ -0,0 +1,74 @@ +package ru.nm17.narodmon.ui.webCamsScreen + +import androidx.compose.foundation.background +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +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.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi +import com.bumptech.glide.integration.compose.GlideImage +import ru.nm17.narodmon.ui.iosevkaFamily + +@OptIn(ExperimentalGlideComposeApi::class) +@Composable +fun WebCamItem(webCamEntity: WebCamUiEntity) { + Box( + modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), + contentAlignment = Alignment.BottomStart + ) { + GlideImage( + model = webCamEntity.imageUrl, + contentDescription = webCamEntity.name, + contentScale = ContentScale.FillHeight, + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .height(240.dp) + .fillMaxWidth() + ) + Row( + modifier = Modifier + .clip( + RoundedCornerShape(bottomEnd = 8.dp, bottomStart = 8.dp) + ) + .background(Color(0f, 0f, 0f, 0.55f)) + .padding(start = 16.dp, end = 16.dp, bottom = 12.dp, top = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Column { + Text( + text = webCamEntity.time, + color = Color.White, + fontFamily = iosevkaFamily + ) + Text( + text = webCamEntity.name, + color = Color.White, + maxLines = 1, + fontFamily = iosevkaFamily + ) + + } + Text( + text = "${webCamEntity.distance} км", + color = Color.White, + fontFamily = iosevkaFamily, + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.End + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamUiEntity.kt b/app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamUiEntity.kt new file mode 100644 index 0000000..e17a48e --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamUiEntity.kt @@ -0,0 +1,13 @@ +package ru.nm17.narodmon.ui.webCamsScreen + +import android.graphics.Bitmap +import androidx.compose.ui.graphics.ImageBitmap + +data class WebCamUiEntity( + val id: Int, + val name: String, + val distance: Int, + val location: String, + val time: String, + val imageUrl: String +) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamsScreen.kt b/app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamsScreen.kt new file mode 100644 index 0000000..98298b9 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/webCamsScreen/WebCamsScreen.kt @@ -0,0 +1,63 @@ +package ru.nm17.narodmon.ui.webCamsScreen + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +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.tooling.preview.Preview +import androidx.compose.ui.viewinterop.AndroidView +import ru.nm17.narodmon.ui.theme.NarodMonTheme + +@Composable +fun WebCamsScreen() { + + var webCams by remember { + mutableStateOf( + listOf( + WebCamUiEntity( + 1, + "Крутая камера", + 1, + "Улица Пушкина, дом Калатушкина, кватира под номером 5", + "12:45", + "https://images-webcams.windy.com/51/1559159251/current/preview/1559159251.jpg?1686320054" + ), + WebCamUiEntity( + 2, + "Крутая камера 2", + 2, + "Улица Пушкина, дом Калатушкина, кватира под номером 5", + "12:45", + "https://images-webcams.windy.com/51/1559159251/current/preview/1559159251.jpg?1686320054" + ), + WebCamUiEntity( + 3, + "Крутая камера 3", + 3, + "Улица Пушкина, дом Калатушкина, кватира под номером 5", + "12:45", + "https://images-webcams.windy.com/51/1559159251/current/preview/1559159251.jpg?1686320054" + ) + ) + ) + } // TODO источник камер + + LazyColumn() { + items(webCams) { + WebCamItem(webCamEntity = it) + } + } +} + +@Preview +@Composable +fun PreviewWebCams() { + NarodMonTheme { + WebCamsScreen() + } +} \ No newline at end of file