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 { plugins {
alias(libs.plugins.androidApplication) alias(libs.plugins.androidApplication)
alias(libs.plugins.kotlinAndroid) alias(libs.plugins.kotlinAndroid)
id("com.google.devtools.ksp")
kotlin("plugin.serialization") version "1.8.21"
} }
android { android {
@ -29,17 +31,17 @@ android {
} }
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_17
} }
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "17"
} }
buildFeatures { buildFeatures {
compose = true compose = true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.4.3" kotlinCompilerExtensionVersion = "1.4.7"
} }
packaging { packaging {
resources { resources {
@ -95,7 +97,7 @@ dependencies {
// To use Kotlin annotation processing tool (kapt) // To use Kotlin annotation processing tool (kapt)
//kapt("androidx.room:room-compiler:$room_version") //kapt("androidx.room:room-compiler:$room_version")
// To use Kotlin Symbol Processing (KSP) // 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 // optional - Kotlin Extensions and Coroutines support for Room
implementation("androidx.room:room-ktx:$room_version") implementation("androidx.room:room-ktx:$room_version")
@ -115,7 +117,7 @@ dependencies {
// optional - Paging 3 Integration // optional - Paging 3 Integration
implementation("androidx.room:room-paging:$room_version") 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 package ru.nm17.narodmon
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent 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.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.Menu 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.Divider
import androidx.compose.material3.DrawerValue import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@ -19,28 +29,76 @@ import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberDrawerState import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable 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.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.tooling.preview.Preview
import androidx.compose.ui.unit.dp 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.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.room.Room 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 kotlinx.coroutines.launch
import ru.nm17.narodmon.db.AppDatabase 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 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() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -48,31 +106,86 @@ class MainActivity : ComponentActivity() {
val db = Room.databaseBuilder( val db = Room.databaseBuilder(
applicationContext, applicationContext,
AppDatabase::class.java, "database-name" AppDatabase::class.java, "data"
).build() ).build()
setContent { setContent {
val navController = rememberNavController() val coScope = rememberCoroutineScope()
//var asd = getPreferences() //var asd = getPreferences()
NarodMonTheme { NarodMonTheme {
NavHost(navController = navController, startDestination = "agreement") { var agreed by remember { mutableStateOf(true) }
composable("agreement") { }
composable("friendslist") { } 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 // 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 @Composable
fun Greeting(name: String, modifier: Modifier = Modifier) { fun Greeting(name: String, modifier: Modifier = Modifier) {
Text( Text(
@ -86,69 +199,7 @@ fun NavHolderEl() {
//NavHost(navController = NavHostController(N), graph =) //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) @Preview(showBackground = true)
@Composable @Composable

View file

@ -2,10 +2,10 @@ package ru.nm17.narodmon.db
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import ru.nm17.narodmon.db.dao.UserDao import ru.nm17.narodmon.db.dao.KvDao
import ru.nm17.narodmon.db.entities.User import ru.nm17.narodmon.db.entities.KVSetting
@Database(entities = [User::class], version = 1) @Database(entities = [KVSetting::class], version = 1)
abstract class AppDatabase : RoomDatabase() { 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.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity @Entity(tableName = "settings")
data class KVSetting( data class KVSetting(
@PrimaryKey val key: String, @PrimaryKey val setting_key: String,
@ColumnInfo(name = "value") val value: 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 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> <string name="app_name">Народный Мониторинг</string>
<!-- TODO: Remove or change this placeholder text --> <!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string> <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> </resources>

View file

@ -3,5 +3,7 @@
plugins { plugins {
alias(libs.plugins.androidApplication) apply false alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.kotlinAndroid) 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 true // Needed to make the Suppress annotation work for the plugins block