Фикс #10 #15
|
@ -74,63 +74,44 @@ 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")
|
||||
|
||||
// optional - Kotlin Extensions and Coroutines support for Room
|
||||
annotationProcessor("androidx.room:room-compiler:$room_version")
|
||||
implementation("androidx.room:room-runtime:$room_version")
|
||||
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")
|
||||
|
||||
|
||||
// For Identity Credential APIs
|
||||
implementation("androidx.security:security-identity-credential:1.0.0-alpha03")
|
||||
|
||||
|
@ -143,10 +124,14 @@ 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")
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,27 +1,16 @@
|
|||
@file:OptIn(
|
||||
ExperimentalMaterial3Api::class, ExperimentalMaterial3Api::class,
|
||||
ExperimentalMaterial3Api::class
|
||||
)
|
||||
|
||||
|
||||
package ru.nm17.narodmon
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.Menu
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -33,64 +22,17 @@ 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.sensorsScreen.SensorsScreen
|
||||
import ru.nm17.narodmon.ui.navHost.AppNavHost
|
||||
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)
|
||||
|
@ -153,11 +95,7 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
} else {
|
||||
AppNavHost()
|
||||
|
||||
}
|
||||
|
||||
// A surface container using the 'background' color from the theme
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
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) {}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
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()
|
||||
}
|
30
app/src/main/java/ru/nm17/narodmon/ui/navHost/AppNavHost.kt
Normal file
|
@ -0,0 +1,30 @@
|
|||
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()
|
||||
}
|
83
app/src/main/java/ru/nm17/narodmon/ui/navHost/MainScreen.kt
Normal file
|
@ -0,0 +1,83 @@
|
|||
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())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package ru.nm17.narodmon.ui.navHost
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import ru.nm17.narodmon.R
|
||||
|
||||
sealed class MainScreenSealed(
|
||||
DarkCat09 marked this conversation as resolved
|
||||
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)
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
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
|
||||
|
@ -13,29 +11,23 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material.icons.rounded.ArrowDropDown
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.rounded.Check
|
||||
import androidx.compose.material.icons.rounded.Person
|
||||
import androidx.compose.material3.BottomSheetScaffold
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.IconButton
|
||||
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
|
||||
|
@ -45,28 +37,23 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import ovh.plrapps.mapcompose.core.debounce
|
||||
import ru.nm17.narodmon.R
|
||||
import ru.nm17.narodmon.db.entities.SensorType
|
||||
import ru.nm17.narodmon.ui.dialogs.FilterSensorsDialog
|
||||
import ru.nm17.narodmon.ui.elements.SensorItem
|
||||
import ru.nm17.narodmon.ui.dialogs.SortSensorsDialog
|
||||
import ru.nm17.narodmon.ui.elements.SensorItem
|
||||
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, ExperimentalAnimationApi::class)
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SensorsScreen(navController: NavController) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
fun SensorsScreen(outerNavController: NavController) {
|
||||
|
||||
var searchQuery by remember { mutableStateOf("") }
|
||||
var searchActive by remember { mutableStateOf(false) }
|
||||
|
@ -119,108 +106,100 @@ fun SensorsScreen(navController: NavController) {
|
|||
mutableStateOf(SheetHeight.ExtraExpanded)
|
||||
}
|
||||
|
||||
BottomSheetScaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
sheetPeekHeight = when (sheetHeight) {
|
||||
SheetHeight.ExtraExpanded -> 256.dp
|
||||
SheetHeight.Expanded -> 128.dp
|
||||
SheetHeight.Hidden -> 0.dp
|
||||
},
|
||||
scaffoldState = scaffoldState,
|
||||
sheetContent = {
|
||||
AnimatedVisibility(visible = scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded) {
|
||||
OutlinedTextField(
|
||||
value = searchQuery,
|
||||
onValueChange = { searchQuery = it },
|
||||
placeholder = { Text(stringResource(R.string.search)) },
|
||||
shape = SearchBarDefaults.inputFieldShape,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
LazyRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterHorizontally),
|
||||
BottomSheetScaffold(modifier = Modifier.fillMaxSize(), sheetPeekHeight = when (sheetHeight) {
|
||||
SheetHeight.ExtraExpanded -> 256.dp
|
||||
SheetHeight.Expanded -> 128.dp
|
||||
SheetHeight.Hidden -> 0.dp
|
||||
}, scaffoldState = scaffoldState, sheetContent = {
|
||||
AnimatedVisibility(visible = scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded) {
|
||||
OutlinedTextField(
|
||||
value = searchQuery,
|
||||
onValueChange = { searchQuery = it },
|
||||
placeholder = { Text(stringResource(R.string.search)) },
|
||||
shape = SearchBarDefaults.inputFieldShape,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
item {
|
||||
FilterChip(
|
||||
selected = false,
|
||||
onClick = { filterShow = true },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_filter),
|
||||
contentDescription = stringResource(id = R.string.sensors_filter)
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
// Icon(
|
||||
// Icons.Filled.ArrowDropDown,
|
||||
// "",
|
||||
// tint = MaterialTheme.colorScheme.onBackground
|
||||
// )
|
||||
},
|
||||
.padding(horizontal = 8.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
|
||||
label = { Text(text = stringResource(R.string.sensors_filter)) },
|
||||
)
|
||||
}
|
||||
item {
|
||||
FilterChip(
|
||||
selected = sortingType.sortingType != SortingTypes.DISTANCE,
|
||||
onClick = { sortingShow = true },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_sort),
|
||||
contentDescription = stringResource(id = R.string.sensors_sorting)
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
LazyRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterHorizontally),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
item {
|
||||
FilterChip(
|
||||
selected = false,
|
||||
onClick = { filterShow = true },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_filter),
|
||||
contentDescription = stringResource(id = R.string.sensors_filter)
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
// Icon(
|
||||
// Icons.Filled.ArrowDropDown,
|
||||
// "",
|
||||
// tint = MaterialTheme.colorScheme.onBackground
|
||||
// )
|
||||
},
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(
|
||||
if (sortingType.sortingType == SortingTypes.DISTANCE) R.string.sensors_sorting
|
||||
else sortingType.stringRes
|
||||
).toChipTitle(),
|
||||
},
|
||||
|
||||
label = { Text(text = stringResource(R.string.sensors_filter)) },
|
||||
)
|
||||
}
|
||||
item {
|
||||
FilterChip(selected = sortingType.sortingType != SortingTypes.DISTANCE,
|
||||
onClick = { sortingShow = true },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_sort),
|
||||
contentDescription = stringResource(id = R.string.sensors_sorting)
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
// Icon(
|
||||
// Icons.Filled.ArrowDropDown,
|
||||
// "",
|
||||
// tint = MaterialTheme.colorScheme.onBackground
|
||||
// )
|
||||
},
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(
|
||||
if (sortingType.sortingType == SortingTypes.DISTANCE) R.string.sensors_sorting
|
||||
else sortingType.stringRes
|
||||
).toChipTitle(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
item {
|
||||
FilterChip(
|
||||
selected = filterMine,
|
||||
onClick = { filterMine = !filterMine },
|
||||
leadingIcon = {
|
||||
if (filterMine) {
|
||||
Icon(
|
||||
Icons.Rounded.Check, contentDescription = ""
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
FilterChip(
|
||||
selected = filterMine,
|
||||
onClick = { filterMine = !filterMine },
|
||||
leadingIcon = {
|
||||
if (filterMine) {
|
||||
Icon(
|
||||
Icons.Rounded.Check,
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
},
|
||||
label = { Text(text = stringResource(R.string.sensors_mine)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
) {
|
||||
items(sensorEntities) { sensor ->
|
||||
SensorItem(sensor)
|
||||
}
|
||||
},
|
||||
label = { Text(text = stringResource(R.string.sensors_mine)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
) {
|
||||
items(sensorEntities) { sensor ->
|
||||
SensorItem(sensor)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
SearchBar(
|
||||
query = searchQuery,
|
||||
|
@ -231,18 +210,24 @@ fun SensorsScreen(navController: NavController) {
|
|||
},
|
||||
onQueryChange = { query -> searchQuery = query },
|
||||
onSearch = { searchActive = false },
|
||||
placeholder = { Text(stringResource(R.string.search)) },
|
||||
placeholder = { Text(stringResource(R.string.search_sensors)) },
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { outerNavController.navigate(MainScreenSealed.Settings.route) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Settings,
|
||||
contentDescription = stringResource(R.string.settings)
|
||||
)
|
||||
}
|
||||
},
|
||||
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 после взаимодействия с картой
|
||||
|
@ -251,25 +236,18 @@ fun SensorsScreen(navController: NavController) {
|
|||
}
|
||||
}
|
||||
if (sortingShow) {
|
||||
SortSensorsDialog(
|
||||
sortingType,
|
||||
onApply = {
|
||||
sortingType = it
|
||||
sortingShow = false
|
||||
},
|
||||
onDismissRequest = {
|
||||
sortingShow = false
|
||||
}
|
||||
)
|
||||
SortSensorsDialog(sortingType, onApply = {
|
||||
sortingType = it
|
||||
sortingShow = false
|
||||
}, onDismissRequest = {
|
||||
sortingShow = false
|
||||
})
|
||||
}
|
||||
if (filterShow) {
|
||||
FilterSensorsDialog(
|
||||
onApply = {
|
||||
// TODO применение фильтров
|
||||
filterShow = false
|
||||
},
|
||||
onDismissRequest = { filterShow = false }
|
||||
)
|
||||
FilterSensorsDialog(onApply = {
|
||||
// TODO применение фильтров
|
||||
filterShow = false
|
||||
}, onDismissRequest = { filterShow = false })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
app/src/main/java/ru/nm17/narodmon/ui/settings/Settings.kt
Normal file
|
@ -0,0 +1,12 @@
|
|||
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)
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
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()
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
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())
|
||||
}
|
||||
}
|
|
@ -10,15 +10,16 @@ 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
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
|
@ -26,23 +27,23 @@ private val LightColorScheme = lightColorScheme(
|
|||
secondary = PurpleGrey80,
|
||||
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),
|
||||
*/
|
||||
/* 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),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun NarodMonTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
|
@ -57,14 +58,16 @@ fun NarodMonTheme(
|
|||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = colorScheme.primary.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
window.statusBarColor = Color.Transparent.toArgb()
|
||||
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
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
|
||||
|
@ -8,13 +7,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.compose.ui.viewinterop.AndroidView
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import ru.nm17.narodmon.ui.theme.NarodMonTheme
|
||||
|
||||
@Composable
|
||||
fun WebCamsScreen() {
|
||||
fun WebCamsScreen(navController: NavController) {
|
||||
nm17 marked this conversation as resolved
nm17
commented
Также тут под вопросом вынос контроллера. Также тут под вопросом вынос контроллера.
DarkCat09
commented
В студии подсвечивается как unused. Да, это в твоём коде экрана датчиков было, мы и копируем)) В студии подсвечивается как unused. Да, это в твоём коде экрана датчиков было, мы и копируем))
mezhendosina
commented
Он вынесен, чтобы можно было из веб-камер уйти в полноэкранное меню просмотра камеры Он вынесен, чтобы можно было из веб-камер уйти в полноэкранное меню просмотра камеры
|
||||
|
||||
var webCams by remember {
|
||||
mutableStateOf(
|
||||
|
@ -58,6 +57,6 @@ fun WebCamsScreen() {
|
|||
@Composable
|
||||
fun PreviewWebCams() {
|
||||
NarodMonTheme {
|
||||
WebCamsScreen()
|
||||
WebCamsScreen(rememberNavController())
|
||||
}
|
||||
}
|
5
app/src/main/res/drawable/ic_home.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<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"/>
|
||||
nm17 marked this conversation as resolved
nm17
commented
А в Material Icon Library нет этих иконок? Было бы супер, если бы мы могли без своих обойтись https://developer.android.com/jetpack/compose/graphics/images/material А в Material Icon Library нет этих иконок? Было бы супер, если бы мы могли без своих обойтись
https://developer.android.com/jetpack/compose/graphics/images/material
https://developer.android.com/reference/kotlin/androidx/compose/material/icons/package-summary
DarkCat09
commented
Официальный список — https://fonts.google.com/icons Сейчас посмотрел, все нужные иконки имеются. > А в Material Icon Library нет этих иконок? Было бы супер, если бы мы могли без своих обойтись
Официальный список — https://fonts.google.com/icons
При выборе иконки там даже есть код для Compose.
Сейчас посмотрел, все нужные иконки имеются.
mezhendosina
commented
Дак я оттуда и брал))0) Дак я оттуда и брал))0)
DarkCat09
commented
А, вижу. Раньше была вкладка с Jetpack Compose, убрали походу. Вот так надо: https://developer.android.com/reference/kotlin/androidx/compose/material/icons/Icons.Outlined
вместо использования своей SVGшки.
Прям во всех стилях (Filled, Outlined, Rounded) нету? Проверить сейчас негде. > Дак я оттуда и брал))0)
А, вижу. Раньше была вкладка с Jetpack Compose, убрали походу.
Вот так надо: https://developer.android.com/reference/kotlin/androidx/compose/material/icons/Icons.Outlined
```kotlin
Icons.Outlined.Home
```
вместо использования своей SVGшки.
> Там нет как минимум иконки камеры
Прям во всех стилях (Filled, Outlined, Rounded) нету? Проверить сейчас негде.
Если так, то печально.
Можем посмотреть альтернативные библиотеки для Compose с теми же материал-диазайновыми иконками.
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_message.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<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>
|
5
app/src/main/res/drawable/ic_settings.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<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>
|
5
app/src/main/res/drawable/ic_webcam.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<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>
|
|
@ -52,4 +52,10 @@
|
|||
<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>
|
||||
</resources>
|
Интересное решение