This commit is contained in:
ityurner02@mail.ru 2023-11-26 22:06:02 +04:00
parent 895ee0fe2b
commit 02a36d1a92
28 changed files with 952 additions and 63 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.10" />
<option name="version" value="1.8.20" />
</component>
</project>

View File

@ -1,6 +1,7 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp")
}
android {
@ -27,17 +28,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.5"
}
packaging {
resources {
@ -57,6 +58,14 @@ dependencies {
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
val room_version = "2.5.2"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
ksp("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-ktx:$room_version")
implementation("androidx.room:room-paging:$room_version")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

View File

@ -1,18 +0,0 @@
package com.example.myapplication.book.model
import java.io.Serializable
import com.example.myapplication.R
data class Book(
val title: String,
val author: String,
val content: String
) : Serializable
fun getBooks(): List<Book> {
return listOf(
Book("1984", "Дж. Оруэлл", "Был холодный ясный апрельский день, и часы пробили тринадцать."),
Book("Собачье сердце", "М.А. Булгаков", "Уу-у-у-у-гу-гуг-гуу! О, гляньте на меня, я погибаю."),
Book("Вельд", "Р. Брэдбери", "— Джорджи, пожалуйста, посмотри детскую комнату. — А что с ней?")
)
}

View File

@ -0,0 +1,55 @@
package com.example.myapplication.composeui
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.material3.Text
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.BookWithAuthor
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun BookRead(id: Int) {
val context = LocalContext.current
val (bookWithAuthor, setBookWithAuthor) = remember { mutableStateOf<BookWithAuthor?>(null) }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
setBookWithAuthor(AppDatabase.getInstance(context).bookDao().getByUid(id))
}
}
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
){
Text(text = bookWithAuthor?.book?.content ?: "", fontSize=22.sp)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun BookReadPreview() {
MyApplicationTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
BookRead(id = 0)
}
}
}

View File

@ -0,0 +1,91 @@
package com.example.myapplication.composeui
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
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.navigation.NavController
import com.example.myapplication.R
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.Book
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun BookSearch(navController: NavController?, text: String) {
val context = LocalContext.current
val books = remember { mutableStateListOf<Book>() }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).bookDao().getBySearch(text).collect { data ->
books.clear()
books.addAll(data)
}
}
}
Column(Modifier.padding(all = 10.dp)) {
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 180.dp)) {
items (books){ book ->
key(book.uid) {
val bookId = Screen.BookView.route.replace("{id}", book.uid.toString())
Image(
painter = painterResource(id = book.cover!!),
contentDescription = book.title,
modifier = Modifier
.height(300.dp)
.padding(horizontal = 5.dp)
.clickable(
enabled = true,
onClick = { navController?.navigate(bookId) }
)
)
}
}
}
Spacer(Modifier.padding(bottom = 10.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = { navController?.navigate(Screen.Loader.route) }) {
Text(stringResource(id = R.string.add_book))
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun BookSearchPreview() {
MyApplicationTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
BookSearch(navController = null, "")
}
}
}

View File

@ -2,49 +2,76 @@ package com.example.myapplication.composeui
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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 androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.withContext
import kotlinx.coroutines.Dispatchers
import com.example.myapplication.db.database.AppDatabase;
import com.example.myapplication.db.model.BookWithAuthor;
import com.example.myapplication.R
import com.example.myapplication.book.model.getBooks
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.ui.theme.MyApplicationTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BookView(id: Int) {
val book = getBooks()[id]
fun BookView(navController: NavController?, id: Int) {
val context = LocalContext.current
val (bookWithAuthor, setBookWithAuthor) = remember { mutableStateOf<BookWithAuthor?>(null) }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
setBookWithAuthor(AppDatabase.getInstance(context).bookDao().getByUid(id))
}
}
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
) {
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = book.title, onValueChange = {}, readOnly = true,
value = bookWithAuthor?.book?.title ?: "", onValueChange = {}, readOnly = true,
label = {
Text(stringResource(id = R.string.book_title))
}
)
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = book.author, onValueChange = {}, readOnly = true,
value = bookWithAuthor?.authorName ?: "", onValueChange = {}, readOnly = true,
label = {
Text(stringResource(id = R.string.author_name))
}
)
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = book.content, onValueChange = {}, readOnly = true,
value = bookWithAuthor?.book?.description ?: "", onValueChange = {}, readOnly = true,
label = {
Text(stringResource(id = R.string.all_content))
Text(stringResource(id = R.string.description))
}
)
Spacer(Modifier.padding(bottom = 10.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = { navController?.navigate(Screen.BookRead.route.replace("{id}", id.toString())) }) {
Text(stringResource(id = R.string.read_book))
}
}
}
@ -56,7 +83,7 @@ fun BookViewPreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
BookView(id = 0)
BookView(navController = null, id = 0)
}
}
}

View File

@ -5,35 +5,68 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.height
import androidx.compose.ui.res.painterResource
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
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 androidx.compose.runtime.remember
import androidx.navigation.NavController
import kotlinx.coroutines.Dispatchers
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.withContext
import com.example.myapplication.db.database.AppDatabase;
import com.example.myapplication.db.model.Book;
import com.example.myapplication.R
import com.example.myapplication.book.model.getBooks
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.ui.theme.MyApplicationTheme
@Composable
fun Catalog(navController: NavController?) {
val context = LocalContext.current
val books = remember { mutableStateListOf<Book>() }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).bookDao().getAll().collect { data ->
books.clear()
books.addAll(data)
}
}
}
Column(Modifier.padding(all = 10.dp)) {
getBooks().forEachIndexed() { index, book ->
val bookId = Screen.BookView.route.replace("{id}", index.toString())
Button(
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 180.dp)) {
items (books){ book ->
key(book.uid) {
val bookId = Screen.BookView.route.replace("{id}", book.uid.toString())
Image(
painter = painterResource(id = book.cover!!),
contentDescription = book.title,
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = { navController?.navigate(bookId) }) {
Text("${book.title}")
.height(300.dp)
.padding(horizontal = 5.dp)
.clickable(
enabled = true,
onClick = { navController?.navigate(bookId) }
)
)
}
}
Spacer(Modifier.padding(bottom = 40.dp))
}
Spacer(Modifier.padding(bottom = 10.dp))
Button(
modifier = Modifier
.fillMaxWidth()

View File

@ -12,6 +12,7 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@ -20,30 +21,57 @@ import androidx.navigation.NavController
import com.example.myapplication.R
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.User
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.launch
import kotlinx.coroutines.GlobalScope
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Enter(navController: NavController?) {
var login by remember{mutableStateOf("")}
var password by remember{mutableStateOf("")}
val context = LocalContext.current
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).userDao().getAll()
}
}
Column(Modifier.fillMaxWidth().padding(all = 40.dp)) {
val loginStr = remember{mutableStateOf("")}
Text(stringResource(id = R.string.login))
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = loginStr.value, onValueChange = { newText -> loginStr.value = newText },
value = login, onValueChange = { login = it },
)
Spacer(Modifier.padding(bottom = 10.dp))
val passwordStr = remember{mutableStateOf("")}
Text(stringResource(id = R.string.password))
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = passwordStr.value, onValueChange = { newText -> passwordStr.value = newText },
value = password, onValueChange = { password = it },
)
Spacer(Modifier.padding(bottom = 20.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = { navController?.navigate(Screen.Profile.route) }) {
onClick = {
var user: User?
GlobalScope.launch (Dispatchers.Main) {
user = AppDatabase.getInstance(context).userDao().tryLogin(login, password)
if (user != null) {
//AppDatabase.getInstance(context).userDao().setLogined(user!!.uid!!)
navController?.navigate(Screen.Profile.route)
} else {
password = ""
login = "Неверный логин или пароль"
}
}
}) {
Text(stringResource(id = R.string.enter))
}
}

View File

@ -0,0 +1,69 @@
package com.example.myapplication.composeui
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.myapplication.R
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.Author
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun ListAuthors() {
val context = LocalContext.current
val authors = remember { mutableStateListOf<Author>() }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).authorDao().getAll().collect { data ->
authors.clear()
authors.addAll(data)
}
}
}
Column(Modifier.padding(all = 10.dp)) {
authors.forEach{ author ->
Text(author.name, fontSize = 20.sp)
}
Spacer(Modifier.padding(bottom = 10.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = {}) {
Text(stringResource(id = R.string.add_book))
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun ListAuthorsPreview() {
MyApplicationTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
ListAuthors()
}
}
}

View File

@ -1,25 +1,254 @@
package com.example.myapplication.composeui
import android.content.res.Configuration
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Column
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.foundation.shape.CircleShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.User
import com.example.myapplication.R
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Profile() {
Column(Modifier.padding(all = 40.dp)) {
Text(stringResource(id = R.string.stats))
fun Profile(navController: NavController?) {
val openDialogEdit = remember { mutableStateOf(false) }
val openDialogExit = remember { mutableStateOf(false) }
val openDialogDelete = remember { mutableStateOf(false) }
val context = LocalContext.current
val user = remember { mutableStateOf<User>(User("", "", "", "USER")) }
val userOldPsswd = remember { mutableStateOf("") }
val userNewPsswd = remember { mutableStateOf("") }
val userNewPsswdConf = remember { mutableStateOf("") }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
user.value = AppDatabase.getInstance(context).userDao().getByUid(2)
}
}
Column(Modifier.padding(all = 10.dp), horizontalAlignment = Alignment.CenterHorizontally) {
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = user.value.login, onValueChange = {user.value.login = it},
label = {
Text(stringResource(id = R.string.login))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = user.value.password, onValueChange = {user.value.password = it},
label = {
Text(stringResource(id = R.string.password))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = user.value.email, onValueChange = {user.value.email = it},
label = {
Text(stringResource(id = R.string.email))
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
Spacer(modifier = Modifier.padding(all = 20.dp))
Button(
onClick = { openDialogEdit.value = true },
shape = CircleShape,
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.primary
)
) {
// Inner content including an icon and a text label
Icon(
imageVector = Icons.Default.Create,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "Редактировать")
}
Button(
onClick = { openDialogExit.value = true },
shape = CircleShape,
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.error
)
) {
// Inner content including an icon and a text label
Icon(
imageVector = Icons.Default.Close,
contentDescription = "Favorite",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "Выход")
}
OutlinedButton(
onClick = { openDialogDelete.value = true },
shape = CircleShape,
modifier = Modifier.fillMaxWidth(fraction = 0.75f),
border= BorderStroke(1.dp, MaterialTheme.colorScheme.error),
colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.error
)
) {
// Inner content including an icon and a text label
Icon(
imageVector = Icons.Outlined.Clear,
contentDescription = "Удалить",
modifier = Modifier.size(20.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(text = "Удалить аккаунт")
}
}
if (openDialogEdit.value) {
AlertDialog(
icon = {
Icon(Icons.Default.Edit, contentDescription = "Example Icon")
},
title = {
Text(text = "Подтверждение")
},
text = {
Text(text = "Вы хотите применить изменения?")
},
onDismissRequest = {
openDialogEdit.value = false
},
confirmButton = {
TextButton(
onClick = {
openDialogEdit.value = false
/* TODO */
}
) {
Text("Да")
}
},
dismissButton = {
TextButton(
onClick = {
openDialogEdit.value = false
}
) {
Text("Нет")
}
}
)
}
if (openDialogExit.value) {
AlertDialog(
icon = {
Icon(Icons.Default.Close, contentDescription = "Example Icon")
},
title = {
Text(text = "Подтверждение")
},
text = {
Text(text = "Вы хотите выйти из аккаунта?")
},
onDismissRequest = {
openDialogExit.value = false
},
confirmButton = {
TextButton(
onClick = {
openDialogExit.value = false
navController?.navigate(Screen.Enter.route)
}
) {
Text("Да")
}
},
dismissButton = {
TextButton(
onClick = {
openDialogExit.value = false
}
) {
Text("Нет")
}
}
)
}
if (openDialogDelete.value) {
AlertDialog(
icon = {
Icon(Icons.Default.Close, contentDescription = "Example Icon")
},
title = {
Text(text = "Подтверждение")
},
text = {
Text(text = "Вы хотите удалить аккаунт?")
},
onDismissRequest = {
openDialogDelete.value = false
},
confirmButton = {
TextButton(
onClick = {
openDialogDelete.value = false
/* TODO */
}
) {
Text("Да")
}
},
dismissButton = {
TextButton(
onClick = {
openDialogDelete.value = false
}
) {
Text("Нет")
}
}
)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@ -29,7 +258,7 @@ fun ProfilePreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
Profile()
Profile(navController = null)
}
}
}

View File

@ -10,10 +10,13 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@ -22,12 +25,16 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.R
import com.example.myapplication.ui.theme.MyApplicationTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Search() {
fun Search(navController: NavController?) {
var searchStr by remember { mutableStateOf("") }
Column(
Modifier
.fillMaxWidth()
@ -37,16 +44,25 @@ fun Search() {
Text(stringResource(id = R.string.book_world), color = Color.White)
}
Spacer(Modifier.padding(bottom = 20.dp))
val searchStr = remember{ mutableStateOf("") }
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = searchStr.value, onValueChange = { newText -> searchStr.value = newText },
TextField(modifier = Modifier.fillMaxWidth(),
value = searchStr, onValueChange = { searchStr = it }, singleLine = true,
placeholder = { Text("Поиск", fontSize = 22.sp) }
)
Spacer(Modifier.padding(bottom = 10.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = {}) {
onClick = {
if(!searchStr.isEmpty()){
navController?.navigate(
Screen.BookSearch.route.replace(
"{text}",
searchStr
)
)
}
}) {
Text(stringResource(id = R.string.search_book))
}
}
@ -60,7 +76,7 @@ fun SearchPreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
Search()
Search(navController=null)
}
}
}

View File

@ -41,6 +41,9 @@ import com.example.myapplication.composeui.Search
import com.example.myapplication.composeui.Loader
import com.example.myapplication.composeui.Catalog
import com.example.myapplication.composeui.BookView
import com.example.myapplication.composeui.BookRead
import com.example.myapplication.composeui.BookSearch
import com.example.myapplication.composeui.ListAuthors
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -113,15 +116,28 @@ fun Navhost(
composable(Screen.Main.route) { Main(navController) }
composable(Screen.Registration.route) { Registration(navController) }
composable(Screen.Enter.route) { Enter(navController) }
composable(Screen.Profile.route) { Profile() }
composable(Screen.Search.route) { Search() }
composable(Screen.Profile.route) { Profile(navController) }
composable(Screen.Search.route) { Search(navController) }
composable(Screen.Loader.route) { Loader() }
composable(Screen.Catalog.route) { Catalog(navController) }
composable(Screen.ListAuthors.route) { ListAuthors() }
composable(
Screen.BookView.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) { backStackEntry ->
backStackEntry.arguments?.let { BookView(it.getInt("id")) }
backStackEntry.arguments?.let { BookView(navController, it.getInt("id")) }
}
composable(
Screen.BookRead.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) { backStackEntry ->
backStackEntry.arguments?.let { BookRead(it.getInt("id")) }
}
composable(
Screen.BookSearch.route,
arguments = listOf(navArgument("text") { type = NavType.StringType })
) { backStackEntry ->
backStackEntry.arguments?.let { BookSearch(navController = navController ,it.getString ("text", "")) }
}
}
}

View File

@ -3,6 +3,7 @@ package com.example.myapplication.composeui.navigation
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Face
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.List
import androidx.compose.material.icons.filled.Search
@ -33,11 +34,20 @@ enum class Screen(
Catalog(
"catalog", R.string.catalog, Icons.Filled.List
),
ListAuthors(
"listauthors", R.string.listauthors, Icons.Filled.Face
),
Loader(
"loader", R.string.loader, showInBottomBar = false
),
BookSearch(
"book-search/{text}", R.string.search, showInBottomBar = false
),
BookView(
"book-view/{id}", R.string.book_view_title, showInBottomBar = false
),
BookRead(
"book-read/{id}", R.string.book_view_title, showInBottomBar = false
);
companion object {
@ -45,6 +55,7 @@ enum class Screen(
Profile,
Search,
Catalog,
ListAuthors
)
fun getItem(route: String): Screen? {
val findRoute = route.split("/").first()

View File

@ -0,0 +1,25 @@
package com.example.myapplication.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.db.model.Author;
import com.example.myapplication.db.model.Book
import kotlinx.coroutines.flow.Flow
@Dao
interface AuthorDao {
@Query("select * from authors order by author_name collate nocase asc")
fun getAll(): Flow<List<Author>>
@Insert
suspend fun insert(author: Author)
@Update
suspend fun update(author: Author)
@Delete
suspend fun delete(author: Author)
}

View File

@ -0,0 +1,37 @@
package com.example.myapplication.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow
import com.example.myapplication.db.model.Book;
import com.example.myapplication.db.model.BookWithAuthor;
@Dao
interface BookDao {
@Query("select * from books order by title collate nocase asc")
fun getAll(): Flow<List<Book>>
@Query("select * from books left join authors on books.author_id = authors.uid where books.uid = :uid")
suspend fun getByUid(uid: Int): BookWithAuthor
@Query("select * from books where books.title LIKE '%' || :searchStr || '%'")
fun getBySearch(searchStr: String): Flow<List<Book>>
@Query("select * from books where books.user_id = :userId")
fun getByUserId(userId: Int): Flow<List<Book>>
@Query("select * from books where books.author_id = :authorId")
fun getByAuthorId(authorId: Int): Flow<List<Book>>
@Insert
suspend fun insert(book: Book)
@Update
suspend fun update(book: Book)
@Delete
suspend fun delete(book: Book)
}

View File

@ -0,0 +1,30 @@
package com.example.myapplication.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow
import com.example.myapplication.db.model.User;
@Dao
interface UserDao {
@Query("select * from users")
fun getAll(): Flow<List<User>>
@Query("select * from users where login = :login and password = :password")
suspend fun tryLogin(login: String, password: String): User?
@Query("select * from users where users.uid = :uid")
suspend fun getByUid(uid: Int): User
@Insert
suspend fun insert(user: User)
@Update
suspend fun update(user: User)
@Delete
suspend fun delete(user: User)
}

View File

@ -0,0 +1,80 @@
package com.example.myapplication.db.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.example.myapplication.db.dao.UserDao;
import com.example.myapplication.db.dao.BookDao;
import com.example.myapplication.db.dao.AuthorDao;
import com.example.myapplication.db.model.User;
import com.example.myapplication.db.model.Book;
import com.example.myapplication.db.model.Author;
import com.example.myapplication.R
@Database(entities = [Book::class, Author::class, User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun bookDao(): BookDao
abstract fun authorDao(): AuthorDao
abstract fun userDao(): UserDao
companion object {
private const val DB_NAME: String = "PMU"
@Volatile
private var INSTANCE: AppDatabase? = null
private suspend fun populateDatabase() {
INSTANCE?.let { database ->
//Users
val userDao = database.userDao()
val user1 = User(1, "Admin", "Admin", "admin@mail.ru", "ADMIN")
val user2 = User(2, "User1", "123", "u1@mail.ru", "USER")
userDao.insert(user1)
userDao.insert(user2)
// Authors
val authorDao = database.authorDao()
val author1 = Author(1, "Дж. Оруэлл")
val author2 = Author(2, "М.А. Булгаков")
val author3 = Author(3, "Р. Брэдбери")
authorDao.insert(author1)
authorDao.insert(author2)
authorDao.insert(author3)
// Books
val bookDao = database.bookDao()
val book1 = Book(1,"1984", "Роман о том, как репрессивная машина тоталитарного государства может уничтожить любую личность.", "Был холодный ясный апрельский день, и часы пробили тринадцать.\n" + "Уинстон Смит, прижав подбородок к груди и ежась от омерзительного ветра, быстро скользнул в стеклянные двери Дома Победы, но все же вихрь песка и пыли успел ворваться вместе с ним.", R.drawable.or1984, 1, 1)
val book2 = Book(2,"Собачье сердце", "Роман о бродячем псе Шарике, превращенном в человека.", "Уу-у-у-у-гу-гуг-гуу! О, гляньте на меня, я погибаю.\n" + "Вьюга в подворотне ревёт мне отходную, и я вою с ней.\n" + "Пропал я, пропал.", R.drawable.dogsheart, 2, 1)
val book3 = Book(3,"Вельд", "Рассказ о зависимости от технических устройств, потере человечности.", "— Джорджи, пожалуйста, посмотри детскую комнату.\n" + "А что с ней?\n" + "Не знаю.\n" + "— Так в чем же дело?", R.drawable.veld, 3, 1)
val book4 = Book(4,"Роковые яйца", "Рассказ, критикующий стремление к прогрессу без учета последствий.","16 апреля 1928 года, вечером, профессор зоологии IV государственного университета и директор зооинститута в Москве Персиков вошел в свой кабинет, помещающийся в зооинституте, что на улице Герцена.\n" + "Профессор зажег верхний матовый шар и огляделся.", R.drawable.eggs, 2, 2)
bookDao.insert(book1)
bookDao.insert(book2)
bookDao.insert(book3)
bookDao.insert(book4)
}
}
fun getInstance(appContext: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
Room.databaseBuilder(
appContext,
AppDatabase::class.java,
DB_NAME
)
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
CoroutineScope(Dispatchers.IO).launch {
populateDatabase()
}
}
})
.build()
.also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,30 @@
package com.example.myapplication.db.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
@Entity(tableName = "authors")
data class Author(
@PrimaryKey(autoGenerate = true)
val uid: Int?,
@ColumnInfo(name = "author_name")
val name: String
) {
@Ignore
constructor(
name: String
) : this(null, name)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Author
if (uid != other.uid) return false
return true
}
override fun hashCode(): Int {
return uid ?: -1
}
}

View File

@ -0,0 +1,65 @@
package com.example.myapplication.db.model
import androidx.annotation.DrawableRes
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import androidx.room.Ignore
@Entity(
tableName = "books", foreignKeys = [
ForeignKey(
entity = Author::class,
parentColumns = ["uid"],
childColumns = ["author_id"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT
),
ForeignKey(
entity = User::class,
parentColumns = ["uid"],
childColumns = ["user_id"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT
)
]
)
data class Book(
@PrimaryKey(autoGenerate = true)
val uid: Int?,
@ColumnInfo(name = "title")
val title: String,
@ColumnInfo(name = "description")
val description: String,
@ColumnInfo(name = "content")
val content: String,
@ColumnInfo(name = "cover")
@DrawableRes val cover: Int?,
@ColumnInfo(name = "author_id", index = true)
val authorId: Int?,
@ColumnInfo(name = "user_id", index = true)
val userId: Int?
) {
@Ignore
constructor(
title: String,
description: String,
content: String,
cover: Int?,
authorId: Int?,
userId: Int?
) : this(null, title, description, content, cover, authorId, userId)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Book
if (uid != other.uid) return false
return true
}
override fun hashCode(): Int {
return uid ?: -1
}
}

View File

@ -0,0 +1,11 @@
package com.example.myapplication.db.model
import androidx.room.ColumnInfo
import androidx.room.Embedded
data class BookWithAuthor(
@Embedded
val book: Book,
@ColumnInfo(name = "author_name")
val authorName: String
)

View File

@ -0,0 +1,41 @@
package com.example.myapplication.db.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.Ignore
import androidx.room.Index
@Entity(tableName = "users", indices = [(Index(value = ["login"], unique = true))])
data class User(
@PrimaryKey(autoGenerate = true)
val uid: Int?,
@ColumnInfo(name = "login")
var login: String,
@ColumnInfo(name = "password")
var password: String,
@ColumnInfo(name = "email")
var email: String,
@ColumnInfo(name = "admin")
var role: String,
) {
@Ignore
constructor(
login: String,
password: String,
email: String,
role: String
) : this(null, login, password, email, role)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as User
if (uid != other.uid) return false
return true
}
override fun hashCode(): Int {
return uid ?: -1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -9,6 +9,7 @@
<string name="search">Поиск</string>
<string name="loader">Загрузка</string>
<string name="catalog">Каталог</string>
<string name="listauthors">Писатели</string>
<string name="book_view_title">Книга</string>
<string name="enter">Войти</string>
<string name="create_acc">Создать аккаунт</string>
@ -27,11 +28,13 @@
</string>
<string name="book_title">Название</string>
<string name="author_name">Автор</string>
<string name="description">Содержание</string>
<string name="all_content">Содержание</string>
<string name="welcome">Добро пожаловать</string>
<string name="book_world">в мир книг!</string>
<string name="search_book">Найти</string>
<string name="load_book">Загрузить</string>
<string name="add_book">Добавить</string>
<string name="read_book">Читать</string>
<string name="help_me_pls">Вы можете помочь нашей библиотеке, загрузив свою книгу.</string>
</resources>

View File

@ -1,5 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
id("com.android.application") version "8.1.2" apply false
id("org.jetbrains.kotlin.android") version "1.8.20" apply false
id("com.google.devtools.ksp") version "1.8.20-1.0.11" apply false
}

Binary file not shown.