feat: добавлен диалог принятия соглашений

This commit is contained in:
nm17 2023-06-03 18:06:33 +04:00
parent b3fb578a01
commit 270f934798
Signed by: nm17
GPG key ID: 3303B70C59145CD4
11 changed files with 318 additions and 120 deletions

View file

@ -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")
}

View file

@ -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

View file

@ -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
}

View file

@ -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<KVSetting>
@Query("SELECT * FROM settings WHERE setting_key = :key")
fun getByKey(key: String): KVSetting?
@Upsert
fun setAll(vararg settings: KVSetting)
@Delete
fun delete(setting: KVSetting)
}

View file

@ -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<User>
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
fun loadAllByIds(userIds: IntArray): List<User>
@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)
}

View file

@ -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,
)

View file

@ -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
)

View file

@ -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))
}
}
)
}

View file

@ -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,
)
}
}

View file

@ -2,4 +2,13 @@
<string name="app_name">Народный Мониторинг</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="accept_agreements">Я принимаю соглашения</string>
<string name="exit">Выйти</string>
<string name="agreement_dialog_text">
Для продолжения использования вы должны принять ниже перечисленные соглашения от проекта Народный Мониторинг.
\nГоворя про наше приложение под андройд, то можете не беспокоится: его исходный код на все 100% является открытым.
</string>
<string name="privacy_policy">Политика конфиденциальности</string>
<string name="user_agreement">Пользовательское соглашение</string>
<string name="agreement_dialog_title">Примите необходимые соглашения</string>
</resources>

View file

@ -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