Compare commits

..

2 commits

75 changed files with 345 additions and 976 deletions

View file

@ -74,43 +74,62 @@ dependencies {
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
//-- Navigation
val nav_version = "2.5.3"
// Java language implementation
implementation("androidx.navigation:navigation-fragment:$nav_version")
implementation("androidx.navigation:navigation-ui:$nav_version")
// Kotlin
implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
// Feature module Support
implementation("androidx.navigation:navigation-dynamic-features-fragment:$nav_version")
// Testing Navigation
androidTestImplementation("androidx.navigation:navigation-testing:$nav_version")
// Jetpack Compose Integration
implementation("androidx.navigation:navigation-compose:$nav_version")
val room_version = "2.5.1"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
//-- Room
// To use Kotlin annotation processing tool (kapt)
//kapt("androidx.room:room-compiler:$room_version")
// To use Kotlin Symbol Processing (KSP)
val room_version = "2.5.1"
ksp("androidx.room:room-compiler:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-runtime:$room_version")
// optional - Kotlin Extensions and Coroutines support for Room
implementation("androidx.room:room-ktx:$room_version")
// optional - Guava support for Room, including Optional and ListenableFuture
implementation("androidx.room:room-guava:$room_version")
// optional - Test helpers
testImplementation("androidx.room:room-testing:$room_version")
// optional - Paging 3 Integration
implementation("androidx.room:room-paging:$room_version")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
//-- Ktor
// Ktor
val ktor_version = "2.3.1"
implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-cio:$ktor_version")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
implementation(platform("dev.forkhandles:forkhandles-bom:2.6.0.0"))
implementation("dev.forkhandles:result4k")
implementation("io.ktor:ktor-client-core:2.3.1")
implementation("io.ktor:ktor-client-okhttp:2.3.1")
implementation("androidx.security:security-crypto-ktx:1.1.0-alpha06")
implementation("androidx.security:security-crypto-ktx:1.1.0-alpha06")
// For Identity Credential APIs
implementation("androidx.security:security-identity-credential:1.0.0-alpha03")
@ -124,14 +143,10 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
//-- Map Compose library
// Map Compose library
implementation("ovh.plrapps:mapcompose:2.7.1")
//-- Glide
implementation("com.github.bumptech.glide:glide:4.14.2")
// Glide
implementation ("com.github.bumptech.glide:glide:4.14.2")
implementation("com.github.bumptech.glide:compose:1.0.0-alpha.1")
}
}

View file

@ -1,16 +1,28 @@
@file:OptIn(
ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class,
ExperimentalMaterial3Api::class
)
package ru.nm17.narodmon
import android.content.pm.ApplicationInfo
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.Menu
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -22,17 +34,65 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.room.Room
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import ru.nm17.narodmon.db.AppDatabase
import ru.nm17.narodmon.db.entities.KVSetting
import ru.nm17.narodmon.ui.dialogs.AgreementDialog
import ru.nm17.narodmon.ui.navHost.AppNavHost
import ru.nm17.narodmon.ui.dialogs.UuidDialog
import ru.nm17.narodmon.ui.sensorsScreen.SensorsScreen
import ru.nm17.narodmon.ui.theme.NarodMonTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppNavHost() {
val navController = rememberNavController()
val coScope = rememberCoroutineScope()
NavHost(navController = navController, startDestination = "sensors") {
composable("agreement") {
}
composable("sensors") {
Scaffold(bottomBar = {
BottomAppBar(actions = {
Image(
Icons.Rounded.Menu,
contentDescription = null
)
}, floatingActionButton = {
FloatingActionButton(onClick = { /*TODO*/ }) {
Image(
Icons.Rounded.Add,
contentDescription = ""
)
}
},
contentPadding = PaddingValues(start = 16.dp)
)
}) {
Column(modifier = Modifier.padding(it)) {
SensorsScreen(navController)
}
}
}
}
/*...*/
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -65,6 +125,10 @@ class MainActivity : ComponentActivity() {
NarodMonTheme {
var agreed by remember { mutableStateOf(true) }
var uuid: String? by remember { mutableStateOf(null) }
val debug = remember {
0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE
}
LaunchedEffect(key1 = Unit, block = {
coScope.launch(Dispatchers.IO) {
@ -74,7 +138,7 @@ class MainActivity : ComponentActivity() {
}
})
if (!agreed) {
if (!agreed && !debug) {
Scaffold {
AgreementDialog {
coScope.launch(Dispatchers.IO) {
@ -93,9 +157,30 @@ class MainActivity : ComponentActivity() {
Text(text = stringResource(R.string.waiting_for_user_agreement))
}
}
} else {
}
else if (debug && uuid == null) {
Scaffold {
UuidDialog { uuidNew ->
uuid = uuidNew
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxSize()
.padding(it)
) {
CircularProgressIndicator()
Text(text = "")
}
}
}
else {
AppNavHost()
}
// A surface container using the 'background' color from the theme
}
}
}

View file

@ -1,16 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities
data class SensorEntity(
val changed: Int,
val fav: Int,
val id: Int,
val mac: String,
val name: String,
val pub: Int,
val time: Int,
val trend: Int,
val type: Int,
val unit: String,
val value: Double
)

View file

@ -1,8 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.addLike
data class AddLikeRequestEntity(
val api_key: String,
val cmd: String,
val id: Int,
val uuid: String
)

View file

@ -1,7 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.addLike
data class AddLikeResponseEntity(
val id: Int,
val liked: Int,
val time: Int
)

View file

@ -1,11 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.appInit
data class AppInitRequestEntity(
val api_key: String,
val cmd: String,
val lang: String,
val platform: String,
val utc: Int,
val uuid: String,
val version: String
)

View file

@ -1,14 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.appInit
data class AppInitResponseEntity(
val addr: String,
val favorites: List<Any>,
val lat: Double,
val latest: String,
val login: String,
val lon: Double,
val timestamp: Int,
val types: List<AppInitTypeEntity>,
val url: String,
val vip: Int
)

View file

@ -1,7 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.appInit
data class AppInitTypeEntity(
val name: String,
val type: Int,
val unit: String
)

View file

@ -1,12 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.bugReport
data class BugReportRequestEntity(
val api_key: String,
val cmd: String,
val email: String,
val logs: String,
val mess: String,
val name: String,
val time: Int,
val uuid: String
)

View file

@ -1,8 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.disLike
data class DisLikeRequestEntity(
val api_key: String,
val cmd: String,
val id: Int,
val uuid: String
)

View file

@ -1,7 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.disLike
data class DisLikeResponseEntity(
val id: Int,
val liked: Int,
val time: Int
)

View file

@ -1,12 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.mapBounds
data class MapBoundsDeviceEntity(
val id: Int,
val lat: Double,
val lon: Double,
val name: String,
val time: Int,
val type: Int,
val unit: String,
val value: Double
)

View file

@ -1,10 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.mapBounds
data class MapBoundsRequestEntity(
val api_key: String,
val bounds: List<Int>,
val cmd: String,
val lang: String,
val limit: Int,
val uuid: String
)

View file

@ -1,6 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.mapBounds
data class MapBoundsResponseEntity(
val devices: List<MapBoundsDeviceEntity>,
val webcams: List<MapBoundsWebcamEntity>
)

View file

@ -1,10 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.mapBounds
data class MapBoundsWebcamEntity(
val id: Int,
val image: String,
val lat: Double,
val lon: Double,
val name: String,
val time: Int
)

View file

@ -1,9 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.nameSensor
data class NameSensorRequestEntity(
val api_key: String,
val cmd: String,
val id: Int,
val name: String,
val uuid: String
)

View file

@ -1,8 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.pubSensor
data class PubSensorRequestEntity(
val api_key: String,
val cmd: String,
val id: Int,
val uuid: String
)

View file

@ -1,6 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.pubSensor
data class PubSensorResponseEntity(
val code: Int,
val id: Int
)

View file

@ -1,9 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sendCommand
data class SendCommandRequestEntity(
val api_key: String,
val cmd: String,
val command: String,
val id: Int,
val uuid: String
)

View file

@ -1,13 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sendComplaint
data class SendComplaintRequestEntity(
val api_key: String,
val cmd: String,
val email: String,
val id: Int,
val name: String,
val problem: String,
val time: Int,
val uuid: String,
val value: Int
)

View file

@ -1,12 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sendMessage
data class SendMessageRequestEntity(
val api_key: String,
val cmd: String,
val email: String,
val mess: String,
val name: String,
val subj: String,
val uid: Int,
val uuid: String
)

View file

@ -1,7 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsHistory
data class HistoryDataEntity(
val id: Int,
val time: Int,
val value: Double
)

View file

@ -1,8 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsHistory
data class HistorySensorEntity(
val id: Int,
val name: String,
val type: Int,
val unit: String
)

View file

@ -1,10 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsHistory
data class SensorsHistoryRequestEntity(
val api_key: String,
val cmd: String,
val id: Int,
val offset: Int,
val period: String,
val uuid: String
)

View file

@ -1,6 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsHistory
data class SensorsHistoryResponseEntity(
val `data`: List<HistoryDataEntity>,
val sensors: List<HistorySensorEntity>
)

View file

@ -1,19 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsNearby
import ru.nm17.narodmon.appNarodMonApiClient.entities.SensorEntity
data class NearbyDeviceEntity(
val cmd: Int,
val distance: Double,
val id: Int,
val lat: Double,
val location: String,
val lon: Double,
val mac: String,
val my: Int,
val name: String,
val owner: String,
val sensors: List<SensorEntity>,
val time: Int
)

View file

@ -1,12 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsNearby
data class SensorsNearbyRequestEntity(
val api_key: String,
val cmd: String,
val lang: String,
val lat: Double,
val lon: Double,
val radius: Int,
val types: List<Int>,
val uuid: String
)

View file

@ -1,5 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsNearby
data class SensorsNearbyResponseEntity(
val devices: List<NearbyDeviceEntity>
)

View file

@ -1,19 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsOnDevice
import ru.nm17.narodmon.ui.entities.SensorEntity
data class SensorOnDeviceEntity(
val cmd: Int,
val distance: Double,
val id: Int,
val info: String,
val location: String,
val mac: String,
val my: Int,
val name: String,
val owner: String,
val photo: String,
val sensors: List<SensorEntity>,
val site: String,
val time: Int
)

View file

@ -1,9 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsOnDevice
data class SensorsOnDeviceRequestEntity(
val api_key: String,
val cmd: String,
val devices: List<Int>,
val lang: String,
val uuid: String
)

View file

@ -1,5 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsOnDevice
data class SensorsOnDeviceResponseEntity(
val devices: List<SensorOnDeviceEntity>
)

View file

@ -1,10 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsValues
data class SensorValueEntity(
val changed: Int,
val id: Int,
val time: Int,
val trend: Int,
val type: Int,
val value: Int
)

View file

@ -1,8 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsValues
data class SensorsValuesRequestEntity(
val api_key: String,
val cmd: String,
val sensors: List<Int>,
val uuid: String
)

View file

@ -1,5 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.sensorsValues
data class SensorsValuesResponseEntity(
val sensors: List<SensorValueEntity>
)

View file

@ -1,9 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userFavorites
data class FavoriteSensorEntity(
val id: Int,
val name: String,
val time: Int,
val type: Int,
val value: Double
)

View file

@ -1,8 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userFavorites
data class FavoriteWebcamEntity(
val id: Int,
val image: String,
val name: String,
val time: Int
)

View file

@ -1,10 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userFavorites
data class UserFavoritesRequestEntity(
val api_key: String,
val cmd: String,
val lang: String,
val sensors: List<Int>,
val uuid: String,
val webcams: List<Int>
)

View file

@ -1,6 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userFavorites
data class UserFavoritesResponseEntity(
val sensors: List<FavoriteSensorEntity>,
val webcams: List<FavoriteWebcamEntity>
)

View file

@ -1,6 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLocation
data class CellEntity(
val bssid: String,
val rssi: Int
)

View file

@ -1,9 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLocation
data class UserLocationByAddrRequestEntity(
val addr: String,
val api_key: String,
val cmd: String,
val lang: String,
val uuid: String
)

View file

@ -1,9 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLocation
data class UserLocationByCellRequestEntity(
val api_key: String,
val cells: List<CellEntity>,
val cmd: String,
val lang: String,
val uuid: String
)

View file

@ -1,10 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLocation
data class UserLocationByCoordRequestEntity(
val api_key: String,
val cmd: String,
val lang: String,
val lat: Double,
val lon: Double,
val uuid: String
)

View file

@ -1,9 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLocation
data class UserLocationByWifiRequestEntity(
val api_key: String,
val cmd: String,
val lang: String,
val uuid: String,
val wifi: List<WifiEntity>
)

View file

@ -1,7 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLocation
data class UserLocationResponseEntity(
val addr: String,
val lat: Double,
val lon: Double
)

View file

@ -1,6 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLocation
data class WifiEntity(
val bssid: String,
val rssi: Int
)

View file

@ -1,10 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLogon
data class UserLogonRequestEntity(
val api_key: String,
val cmd: String,
val hash: String,
val lang: String,
val login: String,
val uuid: String
)

View file

@ -1,8 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLogon
data class UserLogonResponseEntity(
val login: String,
val tz: Int,
val uid: Int,
val vip: Int
)

View file

@ -1,7 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLogout
data class UserLogoutRequestEntity(
val api_key: String,
val cmd: String,
val uuid: String
)

View file

@ -1,6 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.userLogout
data class UserLogoutResponseEntity(
val login: String,
val uid: Int
)

View file

@ -1,14 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.weatherReport
data class WeatherReportRequestEntity(
val api_key: String,
val cmd: String,
val humid: String,
val lang: String,
val lat: Double,
val lon: Double,
val press: String,
val temp: String,
val uuid: String,
val wind: String
)

View file

@ -1,5 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.weatherReport
data class WeatherReportResponseEntity(
val result: String
)

View file

@ -1,6 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.webcamImages
data class WebcamImageEntity(
val image: String,
val time: Int
)

View file

@ -1,9 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.webcamImages
data class WebcamImagesRequestEntity(
val api_key: String,
val cmd: String,
val id: Int,
val limit: Int,
val uuid: String
)

View file

@ -1,9 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.webcamImages
data class WebcamImagesResponseEntity(
val distance: Double,
val id: Int,
val images: List<WebcamImageEntity>,
val location: String,
val name: String
)

View file

@ -1,15 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.webcamsNearby
data class WebcamNearbyEntity(
val distance: Double,
val fav: Int,
val id: Int,
val image: String,
val lat: Double,
val location: String,
val lon: Double,
val my: Int,
val name: String,
val owner: String,
val time: Int
)

View file

@ -1,11 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.webcamsNearby
data class WebcamsNearbyRequestEntity(
val api_key: String,
val cmd: String,
val lang: String,
val lat: Double,
val lon: Double,
val radius: Int,
val uuid: String
)

View file

@ -1,5 +0,0 @@
package ru.nm17.narodmon.appNarodMonApiClient.entities.webcamsNearby
data class WebcamsResponseEntity(
val webcams: List<WebcamNearbyEntity>
)

View file

@ -0,0 +1,59 @@
package ru.nm17.narodmon.ui.dialogs
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
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.unit.dp
import ru.nm17.narodmon.R
import kotlin.system.exitProcess
@ExperimentalMaterial3Api
@Composable
fun UuidDialog(modifier: Modifier = Modifier, onUuidInput: (uuid: String) -> Unit) {
var uuid by remember { mutableStateOf("") }
AlertDialog(
onDismissRequest = { },
title = { Text(text = stringResource(id = R.string.uuid_dialog_title)) },
text = {
TextField(
value = uuid,
onValueChange = { uuid = it },
placeholder = { Text(text = "UUID") }
)
},
confirmButton = {
TextButton(
onClick = { onUuidInput(uuid) }
) {
Text(text = stringResource(id = R.string.uuid_confirm))
}
},
dismissButton = {
TextButton(
onClick = { onUuidInput("") }) {
Text(text = stringResource(id = R.string.uuid_generate))
}
},
modifier = modifier
)
}

View file

@ -1,50 +0,0 @@
package ru.nm17.narodmon.ui.elements
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.Switch
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import ru.nm17.narodmon.R
/**
* Кнопка, которая нужна для настроек.
* @param titleId Id заголовка кнопки
* @param leadingItem Заполнить, когда нужно вставить Composable перед заголовком(например [Icon], [FilterCheckbox] или [Switch]
*/
@Composable
fun SettingsItem(
@StringRes titleId: Int,
leadingItem: @Composable (() -> Unit)? = null,
onClick: () -> Unit = {}
) {
Row(modifier = Modifier.padding(16.dp)) {
if (leadingItem != null) {
leadingItem.invoke()
Spacer(modifier = Modifier.size(16.dp))
}
Column(modifier = Modifier
.fillMaxWidth()
.clickable { onClick.invoke() }) {
Text(text = stringResource(id = titleId))
}
}
}
@Preview(showBackground = true)
@Composable
fun PreviewSettingsItem() {
SettingsItem(R.string.about_app) {}
}

View file

@ -1,16 +0,0 @@
package ru.nm17.narodmon.ui.messagesScreen
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
@Composable
fun MessagesScreen() {
Text(text = "todo")
}
@Preview
@Composable
fun PreviewMessagesScreen() {
MessagesScreen()
}

View file

@ -1,30 +0,0 @@
package ru.nm17.narodmon.ui.navHost
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import ru.nm17.narodmon.ui.settings.SettingsNavigation
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppNavHost() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "main") {
composable("main") {
MainScreen(navController)
}
composable("settings") {
SettingsNavigation()
}
}
}
@Preview
@Composable
fun PreviewAppNavHost() {
AppNavHost()
}

View file

@ -1,83 +0,0 @@
package ru.nm17.narodmon.ui.navHost
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import ru.nm17.narodmon.ui.sensorsScreen.SensorsScreen
import ru.nm17.narodmon.ui.theme.NarodMonTheme
import ru.nm17.narodmon.ui.webCamsScreen.WebCamsScreen
val items = listOf(
MainScreenSealed.Sensors,
MainScreenSealed.Webcams,
MainScreenSealed.Messages
)
@Composable
fun MainScreen(outerNavController: NavController) {
val navController = rememberNavController()
Scaffold(
bottomBar = {
NavigationBar {
items.forEach { screen ->
NavigationBarItem(
selected = navController.currentDestination?.route == screen.route,
onClick = {
navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
label = { Text(text = stringResource(id = screen.resourceId)) },
icon = {
Icon(
painter = painterResource(id = screen.iconId),
contentDescription = ""
)
})
}
}
},
modifier = Modifier.fillMaxSize()
) {
NavHost(
navController,
startDestination = MainScreenSealed.Sensors.route,
Modifier.padding(it)
) {
composable(MainScreenSealed.Sensors.route) {
SensorsScreen(outerNavController)
}
composable(MainScreenSealed.Webcams.route) { WebCamsScreen(navController) }
composable(MainScreenSealed.Messages.route) { }
}
}
}
@Preview
@Composable
fun PreviewMainScreen() {
NarodMonTheme {
MainScreen(rememberNavController())
}
}

View file

@ -1,18 +0,0 @@
package ru.nm17.narodmon.ui.navHost
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import ru.nm17.narodmon.R
sealed class MainScreenSealed(
val route: String,
@StringRes val resourceId: Int,
@DrawableRes val iconId: Int
) {
object Sensors : MainScreenSealed("sensors", R.string.sensors_page_title, R.drawable.ic_home)
object Webcams : MainScreenSealed("webcams", R.string.webcams, R.drawable.ic_webcam)
object Messages : MainScreenSealed("messages", R.string.messages, R.drawable.ic_message)
object Settings : MainScreenSealed("settings", R.string.settings, R.drawable.ic_settings)
}

View file

@ -1,6 +1,8 @@
package ru.nm17.narodmon.ui.sensorsScreen
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
@ -11,23 +13,29 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.rounded.ArrowDropDown
import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.Person
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.SheetValue
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -37,23 +45,28 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import ovh.plrapps.mapcompose.core.debounce
import ru.nm17.narodmon.R
import ru.nm17.narodmon.db.entities.SensorType
import ru.nm17.narodmon.ui.dialogs.FilterSensorsDialog
import ru.nm17.narodmon.ui.dialogs.SortSensorsDialog
import ru.nm17.narodmon.ui.elements.SensorItem
import ru.nm17.narodmon.ui.dialogs.SortSensorsDialog
import ru.nm17.narodmon.ui.elements.TileMap
import ru.nm17.narodmon.ui.entities.SensorEntity
import ru.nm17.narodmon.ui.entities.SensorSortingUiEntity
import ru.nm17.narodmon.ui.entities.SortingTypes
import ru.nm17.narodmon.ui.navHost.MainScreenSealed
import ru.nm17.narodmon.ui.theme.NarodMonTheme
import ru.nm17.narodmon.ui.toChipTitle
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
@Composable
fun SensorsScreen(outerNavController: NavController) {
fun SensorsScreen(navController: NavController) {
val coroutineScope = rememberCoroutineScope()
var searchQuery by remember { mutableStateOf("") }
var searchActive by remember { mutableStateOf(false) }
@ -106,11 +119,15 @@ fun SensorsScreen(outerNavController: NavController) {
mutableStateOf(SheetHeight.ExtraExpanded)
}
BottomSheetScaffold(modifier = Modifier.fillMaxSize(), sheetPeekHeight = when (sheetHeight) {
BottomSheetScaffold(
modifier = Modifier.fillMaxSize(),
sheetPeekHeight = when (sheetHeight) {
SheetHeight.ExtraExpanded -> 256.dp
SheetHeight.Expanded -> 128.dp
SheetHeight.Hidden -> 0.dp
}, scaffoldState = scaffoldState, sheetContent = {
},
scaffoldState = scaffoldState,
sheetContent = {
AnimatedVisibility(visible = scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded) {
OutlinedTextField(
value = searchQuery,
@ -151,7 +168,8 @@ fun SensorsScreen(outerNavController: NavController) {
)
}
item {
FilterChip(selected = sortingType.sortingType != SortingTypes.DISTANCE,
FilterChip(
selected = sortingType.sortingType != SortingTypes.DISTANCE,
onClick = { sortingShow = true },
leadingIcon = {
Icon(
@ -173,7 +191,8 @@ fun SensorsScreen(outerNavController: NavController) {
else sortingType.stringRes
).toChipTitle(),
)
})
}
)
}
item {
@ -183,7 +202,8 @@ fun SensorsScreen(outerNavController: NavController) {
leadingIcon = {
if (filterMine) {
Icon(
Icons.Rounded.Check, contentDescription = ""
Icons.Rounded.Check,
contentDescription = ""
)
}
},
@ -199,7 +219,8 @@ fun SensorsScreen(outerNavController: NavController) {
SensorItem(sensor)
}
}
}) {
}
) {
Box(modifier = Modifier.fillMaxSize()) {
SearchBar(
query = searchQuery,
@ -210,24 +231,18 @@ fun SensorsScreen(outerNavController: NavController) {
},
onQueryChange = { query -> searchQuery = query },
onSearch = { searchActive = false },
placeholder = { Text(stringResource(R.string.search_sensors)) },
trailingIcon = {
IconButton(onClick = { outerNavController.navigate(MainScreenSealed.Settings.route) }) {
Icon(
Icons.Outlined.Settings,
contentDescription = stringResource(R.string.settings)
)
}
},
placeholder = { Text(stringResource(R.string.search)) },
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = if (!searchActive) 8.dp else 0.dp,
vertical = if (!searchActive) 16.dp else 0.dp
)
) {}
TileMap(
modifier = Modifier.fillMaxSize()
modifier = Modifier
.fillMaxSize()
) {
sheetHeight =
SheetHeight.Expanded // TODO придумать, чтобы менялось на SheetHeight.ExtraExpanded после взаимодействия с картой
@ -236,18 +251,25 @@ fun SensorsScreen(outerNavController: NavController) {
}
}
if (sortingShow) {
SortSensorsDialog(sortingType, onApply = {
SortSensorsDialog(
sortingType,
onApply = {
sortingType = it
sortingShow = false
}, onDismissRequest = {
},
onDismissRequest = {
sortingShow = false
})
}
)
}
if (filterShow) {
FilterSensorsDialog(onApply = {
FilterSensorsDialog(
onApply = {
// TODO применение фильтров
filterShow = false
}, onDismissRequest = { filterShow = false })
},
onDismissRequest = { filterShow = false }
)
}
}

View file

@ -1,12 +0,0 @@
package ru.nm17.narodmon.ui.settings
import androidx.annotation.StringRes
import ru.nm17.narodmon.R
sealed class Settings(val route: String, @StringRes val resourceId: Int) {
object Main : Settings("settings_main", R.string.settings)
object AboutApp : Settings("settings_about_app", R.string.about_app)
object Debug : Settings("settings_debug_menu", R.string.debug_menu)
}

View file

@ -1,39 +0,0 @@
package ru.nm17.narodmon.ui.settings
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsNavigation() {
val navController = rememberNavController()
Scaffold(topBar = {
TopAppBar(title = { Text(text = navController.currentDestination?.route ?: "") })
}) {
NavHost(
navController = navController,
startDestination = Settings.Main.route,
modifier = Modifier.padding(it)
) {
composable(Settings.Main.route) { SettingsScreen(navController) }
composable(Settings.AboutApp.route) { }
composable(Settings.Debug.route) { }
}
}
}
@Preview
@Composable
fun PreviewSettingsNavigation() {
SettingsNavigation()
}

View file

@ -1,37 +0,0 @@
package ru.nm17.narodmon.ui.settings
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import ru.nm17.narodmon.R
import ru.nm17.narodmon.ui.elements.SettingsItem
import ru.nm17.narodmon.ui.theme.NarodMonTheme
@Composable
fun SettingsScreen(navController: NavController) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
item {
SettingsItem(titleId = R.string.debug_menu) {
navController.navigate(Settings.Debug.route)
}
}
item {
SettingsItem(R.string.about_app) {
navController.navigate(Settings.AboutApp.route)
}
}
}
}
@Preview(showBackground = true, showSystemUi = false)
@Composable
fun PreviewSettingsScreen() {
NarodMonTheme {
SettingsScreen(rememberNavController())
}
}

View file

@ -10,7 +10,6 @@ import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
@ -28,14 +27,14 @@ private val LightColorScheme = lightColorScheme(
tertiary = Pink80
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
@ -58,10 +57,8 @@ fun NarodMonTheme(
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = Color.Transparent.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}

View file

@ -1,5 +1,6 @@
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
@ -7,13 +8,13 @@ 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.navigation.NavController
import androidx.navigation.compose.rememberNavController
import androidx.compose.ui.viewinterop.AndroidView
import ru.nm17.narodmon.ui.theme.NarodMonTheme
@Composable
fun WebCamsScreen(navController: NavController) {
fun WebCamsScreen() {
var webCams by remember {
mutableStateOf(
@ -57,6 +58,6 @@ fun WebCamsScreen(navController: NavController) {
@Composable
fun PreviewWebCams() {
NarodMonTheme {
WebCamsScreen(rememberNavController())
WebCamsScreen()
}
}

View file

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,5.69l5,4.5V18h-2v-6H9v6H7v-7.81l5,-4.5M12,3L2,12h3v8h6v-6h2v6h6v-8h3L12,3z"/>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M4,4h16v12L5.17,16L4,17.17L4,4m0,-2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2L4,2zM6,12h12v2L6,14v-2zM6,9h12v2L6,11L6,9zM6,6h12v2L6,8L6,6z"/>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98 0,-0.34 -0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.09,-0.16 -0.26,-0.25 -0.44,-0.25 -0.06,0 -0.12,0.01 -0.17,0.03l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.06,-0.02 -0.12,-0.03 -0.18,-0.03 -0.17,0 -0.34,0.09 -0.43,0.25l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98 0,0.33 0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.09,0.16 0.26,0.25 0.44,0.25 0.06,0 0.12,-0.01 0.17,-0.03l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.06,0.02 0.12,0.03 0.18,0.03 0.17,0 0.34,-0.09 0.43,-0.25l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM17.45,11.27c0.04,0.31 0.05,0.52 0.05,0.73 0,0.21 -0.02,0.43 -0.05,0.73l-0.14,1.13 0.89,0.7 1.08,0.84 -0.7,1.21 -1.27,-0.51 -1.04,-0.42 -0.9,0.68c-0.43,0.32 -0.84,0.56 -1.25,0.73l-1.06,0.43 -0.16,1.13 -0.2,1.35h-1.4l-0.19,-1.35 -0.16,-1.13 -1.06,-0.43c-0.43,-0.18 -0.83,-0.41 -1.23,-0.71l-0.91,-0.7 -1.06,0.43 -1.27,0.51 -0.7,-1.21 1.08,-0.84 0.89,-0.7 -0.14,-1.13c-0.03,-0.31 -0.05,-0.54 -0.05,-0.74s0.02,-0.43 0.05,-0.73l0.14,-1.13 -0.89,-0.7 -1.08,-0.84 0.7,-1.21 1.27,0.51 1.04,0.42 0.9,-0.68c0.43,-0.32 0.84,-0.56 1.25,-0.73l1.06,-0.43 0.16,-1.13 0.2,-1.35h1.39l0.19,1.35 0.16,1.13 1.06,0.43c0.43,0.18 0.83,0.41 1.23,0.71l0.91,0.7 1.06,-0.43 1.27,-0.51 0.7,1.21 -1.07,0.85 -0.89,0.7 0.14,1.13zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
</vector>

View file

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M15,8v8H5V8h10m1,-2H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4V7c0,-0.55 -0.45,-1 -1,-1z"/>
</vector>

View file

@ -52,10 +52,7 @@
<string name="apply">Применить</string>
<string name="sort_by_distance_desc">От дальних к ближним</string>
<string name="cancel1">Отменить</string>
<string name="webcams">Веб-камеры</string>
<string name="messages">Сообщения</string>
<string name="settings">Настройки</string>
<string name="search_sensors">Поиск датчиков</string>
<string name="about_app">О приложении</string>
<string name="debug_menu">Debug-меню</string>
<string name="uuid_dialog_title">Введите UUID приложения</string>
<string name="uuid_confirm">Сохранить</string>
<string name="uuid_generate">Сгенерировать</string>
</resources>

View file

@ -1,5 +1,5 @@
[versions]
agp = "8.2.0-alpha07"
agp = "8.2.0-alpha08"
kotlin = "1.8.10"
core-ktx = "1.9.0"
junit = "4.13.2"