From 270f9347985224bf9adf32a7adf45939a17d5699 Mon Sep 17 00:00:00 2001 From: nm17 Date: Sat, 3 Jun 2023 18:06:33 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=B4=D0=B8=D0=B0=D0=BB=D0=BE=D0=B3=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D1=8F=D1=82=D0=B8=D1=8F=20=D1=81=D0=BE=D0=B3?= =?UTF-8?q?=D0=BB=D0=B0=D1=88=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 14 +- .../java/ru/nm17/narodmon/MainActivity.kt | 189 +++++++++++------- .../java/ru/nm17/narodmon/db/AppDatabase.kt | 8 +- .../java/ru/nm17/narodmon/db/dao/KvDao.kt | 22 ++ .../java/ru/nm17/narodmon/db/dao/UserDao.kt | 26 --- .../ru/nm17/narodmon/db/entities/KVSetting.kt | 6 +- .../java/ru/nm17/narodmon/db/entities/User.kt | 12 -- .../narodmon/ui/elements/AgreementDialog.kt | 89 +++++++++ .../ru/nm17/narodmon/ui/elements/Scaffolds.kt | 61 ++++++ app/src/main/res/values/strings.xml | 9 + build.gradle.kts | 2 + 11 files changed, 318 insertions(+), 120 deletions(-) create mode 100644 app/src/main/java/ru/nm17/narodmon/db/dao/KvDao.kt delete mode 100644 app/src/main/java/ru/nm17/narodmon/db/dao/UserDao.kt delete mode 100644 app/src/main/java/ru/nm17/narodmon/db/entities/User.kt create mode 100644 app/src/main/java/ru/nm17/narodmon/ui/elements/AgreementDialog.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e4d3e61..217e093 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,6 +3,8 @@ plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.kotlinAndroid) + id("com.google.devtools.ksp") + kotlin("plugin.serialization") version "1.8.21" } android { @@ -29,17 +31,17 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "17" } buildFeatures { compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.4.3" + kotlinCompilerExtensionVersion = "1.4.7" } packaging { resources { @@ -95,7 +97,7 @@ dependencies { // To use Kotlin annotation processing tool (kapt) //kapt("androidx.room:room-compiler:$room_version") // To use Kotlin Symbol Processing (KSP) - //ksp("androidx.room:room-compiler:$room_version") + ksp("androidx.room:room-compiler:$room_version") // optional - Kotlin Extensions and Coroutines support for Room implementation("androidx.room:room-ktx:$room_version") @@ -115,7 +117,7 @@ dependencies { // optional - Paging 3 Integration implementation("androidx.room:room-paging:$room_version") - + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") } \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/MainActivity.kt b/app/src/main/java/ru/nm17/narodmon/MainActivity.kt index acd614e..3e7fa3a 100644 --- a/app/src/main/java/ru/nm17/narodmon/MainActivity.kt +++ b/app/src/main/java/ru/nm17/narodmon/MainActivity.kt @@ -4,14 +4,24 @@ package ru.nm17.narodmon +import android.content.Intent +import android.net.Uri import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.clickable +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.Add +import androidx.compose.material.icons.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Divider import androidx.compose.material3.DrawerValue import androidx.compose.material3.ExperimentalMaterial3Api @@ -19,28 +29,76 @@ import androidx.compose.material3.FabPosition import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberDrawerState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.navigation.NavController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.room.Room +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.single import kotlinx.coroutines.launch import ru.nm17.narodmon.db.AppDatabase +import ru.nm17.narodmon.db.entities.KVSetting +import ru.nm17.narodmon.ui.elements.AgreementDialog +import ru.nm17.narodmon.ui.elements.GenericNavScaffold import ru.nm17.narodmon.ui.theme.NarodMonTheme +@Composable +fun AppNavHost() { + val navController = rememberNavController() + val coScope = rememberCoroutineScope() + + + + NavHost(navController = navController, startDestination = "sensors") { + composable("agreement") { + + } + composable("sensors") { + + SensorsPage(navController) + } + + /*...*/ + } +} + class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -48,31 +106,86 @@ class MainActivity : ComponentActivity() { val db = Room.databaseBuilder( applicationContext, - AppDatabase::class.java, "database-name" + AppDatabase::class.java, "data" ).build() + + + setContent { - val navController = rememberNavController() + val coScope = rememberCoroutineScope() + + + + + //var asd = getPreferences() NarodMonTheme { - NavHost(navController = navController, startDestination = "agreement") { - composable("agreement") { } - composable("friendslist") { } - /*...*/ + var agreed by remember { mutableStateOf(true) } + + LaunchedEffect(key1 = "first_agreement_check", block = { + coScope.launch(Dispatchers.IO) { + if (db.kvDao().getByKey("agreement_accepted")?.value != "true") { + agreed = false + } + } + }) + + if (!agreed) { + AgreementDialog { + coScope.launch(Dispatchers.IO) { + db.kvDao().setAll(KVSetting("agreement_accepted", "true")) + agreed = true + } + } } + if (!agreed) { + Surface { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxSize() + ) { + CircularProgressIndicator() + Text(text = "вы не должны видеть этот текст") + } + } + + } else { + AppNavHost() + } + + + // A surface container using the 'background' color from the theme - Scaf({ Text("Content", modifier = Modifier.padding(it)) }) + } } } } +@Composable +fun SensorsPage(navController: NavController) { + GenericNavScaffold(navDrawerSheet = {ModalDrawerSheet { + Text("Drawer title", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleLarge) + Divider() + NavigationDrawerItem( + label = { Text(text = "Drawer Item") }, + selected = true, + onClick = { /*TODO*/ } + ) + // ...other drawer items + }}) { + Greeting(name = "Hello world", modifier = Modifier.padding(it)) + } +} + @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( @@ -86,69 +199,7 @@ fun NavHolderEl() { //NavHost(navController = NavHostController(N), graph =) } -@Composable -fun Scaf(content: @Composable (PaddingValues) -> Unit) { - var expanded = rememberDrawerState(initialValue = DrawerValue.Closed) - var coScope = rememberCoroutineScope(); - /*DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false } - ) { - DropdownMenuItem( - text = { Text("Refresh") }, - onClick = { /* Handle refresh! */ } - ) - DropdownMenuItem( - text = { Text("Settings") }, - onClick = { /* Handle settings! */ } - ) - Divider() - DropdownMenuItem( - text = { Text("Send Feedback") }, - onClick = { /* Handle send feedback! */ } - ) - }*/ - ModalNavigationDrawer(drawerState = expanded, drawerContent = { - ModalDrawerSheet { - Text("Drawer title", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleLarge) - Divider() - NavigationDrawerItem( - label = { Text(text = "Drawer Item") }, - selected = true, - onClick = { /*TODO*/ } - ) - // ...other drawer items - } - - }) { - Scaffold( - topBar = { - TopAppBar( - title = { Text("Top App Bar") }, - colors = TopAppBarDefaults.largeTopAppBarColors( - containerColor = MaterialTheme.colorScheme.primaryContainer - ), - navigationIcon = { - IconButton(onClick = { coScope.launch { expanded.open() } }) { - Icon(Icons.Filled.Menu, contentDescription = null) - } - }, - ) - }, - floatingActionButtonPosition = FabPosition.End, - floatingActionButton = { - FloatingActionButton(onClick = {}) { - Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon") - } - }, - //drawerContent = { Text(text = "Drawer Menu 1") }, - content = content, - - ) - } - -} @Preview(showBackground = true) @Composable diff --git a/app/src/main/java/ru/nm17/narodmon/db/AppDatabase.kt b/app/src/main/java/ru/nm17/narodmon/db/AppDatabase.kt index 47e962c..3d40dcf 100644 --- a/app/src/main/java/ru/nm17/narodmon/db/AppDatabase.kt +++ b/app/src/main/java/ru/nm17/narodmon/db/AppDatabase.kt @@ -2,10 +2,10 @@ package ru.nm17.narodmon.db import androidx.room.Database import androidx.room.RoomDatabase -import ru.nm17.narodmon.db.dao.UserDao -import ru.nm17.narodmon.db.entities.User +import ru.nm17.narodmon.db.dao.KvDao +import ru.nm17.narodmon.db.entities.KVSetting -@Database(entities = [User::class], version = 1) +@Database(entities = [KVSetting::class], version = 1) abstract class AppDatabase : RoomDatabase() { - abstract fun userDao(): UserDao + abstract fun kvDao(): KvDao } diff --git a/app/src/main/java/ru/nm17/narodmon/db/dao/KvDao.kt b/app/src/main/java/ru/nm17/narodmon/db/dao/KvDao.kt new file mode 100644 index 0000000..d03b5a4 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/db/dao/KvDao.kt @@ -0,0 +1,22 @@ +package ru.nm17.narodmon.db.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Query +import androidx.room.Upsert +import ru.nm17.narodmon.db.entities.KVSetting + +@Dao +interface KvDao { + @Query("SELECT * FROM settings") + fun getAll(): List + + @Query("SELECT * FROM settings WHERE setting_key = :key") + fun getByKey(key: String): KVSetting? + + @Upsert + fun setAll(vararg settings: KVSetting) + + @Delete + fun delete(setting: KVSetting) +} diff --git a/app/src/main/java/ru/nm17/narodmon/db/dao/UserDao.kt b/app/src/main/java/ru/nm17/narodmon/db/dao/UserDao.kt deleted file mode 100644 index 4666858..0000000 --- a/app/src/main/java/ru/nm17/narodmon/db/dao/UserDao.kt +++ /dev/null @@ -1,26 +0,0 @@ -package ru.nm17.narodmon.db.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Query -import ru.nm17.narodmon.db.entities.User - -@Dao -interface UserDao { - @Query("SELECT * FROM user") - fun getAll(): List - - @Query("SELECT * FROM user WHERE uid IN (:userIds)") - fun loadAllByIds(userIds: IntArray): List - - @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + - "last_name LIKE :last LIMIT 1") - fun findByName(first: String, last: String): User - - @Insert - fun insertAll(vararg users: User) - - @Delete - fun delete(user: User) -} diff --git a/app/src/main/java/ru/nm17/narodmon/db/entities/KVSetting.kt b/app/src/main/java/ru/nm17/narodmon/db/entities/KVSetting.kt index 1bf699c..fe7eaa6 100644 --- a/app/src/main/java/ru/nm17/narodmon/db/entities/KVSetting.kt +++ b/app/src/main/java/ru/nm17/narodmon/db/entities/KVSetting.kt @@ -4,8 +4,8 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -@Entity +@Entity(tableName = "settings") data class KVSetting( - @PrimaryKey val key: String, - @ColumnInfo(name = "value") val value: String, + @PrimaryKey val setting_key: String, + @ColumnInfo(name = "setting_value") val value: String, ) diff --git a/app/src/main/java/ru/nm17/narodmon/db/entities/User.kt b/app/src/main/java/ru/nm17/narodmon/db/entities/User.kt deleted file mode 100644 index 78658eb..0000000 --- a/app/src/main/java/ru/nm17/narodmon/db/entities/User.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ru.nm17.narodmon.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity -data class User( - @PrimaryKey val uid: Int, - @ColumnInfo(name = "first_name") val firstName: String, - @ColumnInfo(name = "last_name") val lastName: String -) diff --git a/app/src/main/java/ru/nm17/narodmon/ui/elements/AgreementDialog.kt b/app/src/main/java/ru/nm17/narodmon/ui/elements/AgreementDialog.kt new file mode 100644 index 0000000..a525644 --- /dev/null +++ b/app/src/main/java/ru/nm17/narodmon/ui/elements/AgreementDialog.kt @@ -0,0 +1,89 @@ +@file:OptIn(ExperimentalMaterial3Api::class) + +package ru.nm17.narodmon.ui.elements + +import android.content.Context +import android.content.Intent +import android.net.Uri +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.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.activity.ComponentActivity +import androidx.compose.ui.platform.LocalUriHandler +import androidx.core.content.ContextCompat.startActivity +import ru.nm17.narodmon.R +import kotlin.system.exitProcess + +@Composable +fun AgreementDialog(onClick: () -> Unit) { + val uriHandler = LocalUriHandler.current + + AlertDialog( + onDismissRequest = { /* Ничего не делаем пока пользователь не примет соглашение. */ }, + title = { Text(text = stringResource(id = R.string.agreement_dialog_title)) }, + text = { + Column { + Text(text = stringResource(id = R.string.agreement_dialog_text)) + Divider(Modifier.padding(vertical = 8.dp)) + ListItem( + headlineText = { + Text( + text = stringResource(id = R.string.privacy_policy), + style = MaterialTheme.typography.titleSmall + ) + }, + trailingContent = { + Icon( + Icons.Default.KeyboardArrowRight, + contentDescription = "" + ) + }, + modifier = Modifier.clickable { uriHandler.openUri("https://narodmon.ru/#privacy") } + ) + + ListItem( + headlineText = { + Text( + text = stringResource(id = R.string.user_agreement), + style = MaterialTheme.typography.titleSmall + ) + }, + trailingContent = { + Icon( + Icons.Default.KeyboardArrowRight, + contentDescription = "" + ) + }, + modifier = Modifier.clickable { Modifier.clickable { uriHandler.openUri("https://narodmon.ru/#privacy") } } + ) + } + }, + confirmButton = { + TextButton( + onClick = onClick) { + Text(text = stringResource(id = R.string.accept_agreements)) + } + }, + dismissButton = { + TextButton( + onClick = { exitProcess(0) }) { + Text(text = stringResource(id = R.string.exit)) + } + } + + ) +} \ No newline at end of file diff --git a/app/src/main/java/ru/nm17/narodmon/ui/elements/Scaffolds.kt b/app/src/main/java/ru/nm17/narodmon/ui/elements/Scaffolds.kt index 69e1b9a..a8c1ec7 100644 --- a/app/src/main/java/ru/nm17/narodmon/ui/elements/Scaffolds.kt +++ b/app/src/main/java/ru/nm17/narodmon/ui/elements/Scaffolds.kt @@ -1,2 +1,63 @@ +@file:OptIn(ExperimentalMaterial3Api::class) + package ru.nm17.narodmon.ui.elements +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.DrawerValue +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FabPosition +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalNavigationDrawer +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberDrawerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import kotlinx.coroutines.launch + +/** + * Простой скаффолд с навигацией. + * + * TODO: Использовать для датчиков + */ +@Composable +fun GenericNavScaffold(navDrawerSheet: @Composable () -> Unit, content: @Composable (PaddingValues) -> Unit) { + var expanded = rememberDrawerState(initialValue = DrawerValue.Closed) + var coScope = rememberCoroutineScope(); + + ModalNavigationDrawer(drawerState = expanded, drawerContent = navDrawerSheet) { + Scaffold( + topBar = { + TopAppBar( + title = { Text("Top App Bar") }, + colors = TopAppBarDefaults.largeTopAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer + ), + navigationIcon = { + IconButton(onClick = { coScope.launch { expanded.open() } }) { + Icon(Icons.Filled.Menu, contentDescription = null) + } + }, + ) + }, + floatingActionButtonPosition = FabPosition.End, + floatingActionButton = { + FloatingActionButton(onClick = {}) { + Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon") + } + }, + //drawerContent = { Text(text = "Drawer Menu 1") }, + content = content, + + ) + } + +} \ 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 7095c8f..51857ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,4 +2,13 @@ Народный Мониторинг Hello blank fragment + Я принимаю соглашения + Выйти + + Для продолжения использования вы должны принять ниже перечисленные соглашения от проекта Народный Мониторинг. + \nГоворя про наше приложение под андройд, то можете не беспокоится: его исходный код на все 100% является открытым. + + Политика конфиденциальности + Пользовательское соглашение + Примите необходимые соглашения \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 20d87a7..b82cb02 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,5 +3,7 @@ plugins { alias(libs.plugins.androidApplication) apply false alias(libs.plugins.kotlinAndroid) apply false + id("com.google.devtools.ksp") version "1.8.21-1.0.11" apply false + } true // Needed to make the Suppress annotation work for the plugins block \ No newline at end of file