diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 0fc3113..69e8615 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5b9e049..934b019 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") diff --git a/app/src/main/java/com/example/myapplication/book/model/Book.kt b/app/src/main/java/com/example/myapplication/book/model/Book.kt deleted file mode 100644 index 26677a3..0000000 --- a/app/src/main/java/com/example/myapplication/book/model/Book.kt +++ /dev/null @@ -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 { - return listOf( - Book("1984", "Дж. Оруэлл", "Был холодный ясный апрельский день, и часы пробили тринадцать."), - Book("Собачье сердце", "М.А. Булгаков", "У‑у-у-у-у-гу-гуг-гуу! О, гляньте на меня, я погибаю."), - Book("Вельд", "Р. Брэдбери", "— Джорджи, пожалуйста, посмотри детскую комнату. — А что с ней?") - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/composeui/BookRead.kt b/app/src/main/java/com/example/myapplication/composeui/BookRead.kt new file mode 100644 index 0000000..4e88681 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/composeui/BookRead.kt @@ -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(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) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/composeui/BookSearch.kt b/app/src/main/java/com/example/myapplication/composeui/BookSearch.kt new file mode 100644 index 0000000..17dcf1b --- /dev/null +++ b/app/src/main/java/com/example/myapplication/composeui/BookSearch.kt @@ -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() } + 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, "") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/composeui/BookView.kt b/app/src/main/java/com/example/myapplication/composeui/BookView.kt index 1d68c84..450d194 100644 --- a/app/src/main/java/com/example/myapplication/composeui/BookView.kt +++ b/app/src/main/java/com/example/myapplication/composeui/BookView.kt @@ -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(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) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/composeui/Catalog.kt b/app/src/main/java/com/example/myapplication/composeui/Catalog.kt index 51ceee0..ba6c9d6 100644 --- a/app/src/main/java/com/example/myapplication/composeui/Catalog.kt +++ b/app/src/main/java/com/example/myapplication/composeui/Catalog.kt @@ -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?) { - Column(Modifier.padding(all = 10.dp)) { - getBooks().forEachIndexed() { index, book -> - val bookId = Screen.BookView.route.replace("{id}", index.toString()) - Button( - modifier = Modifier - .fillMaxWidth() - .padding(all = 10.dp), - onClick = { navController?.navigate(bookId) }) { - Text("${book.title}") + val context = LocalContext.current + val books = remember { mutableStateListOf() } + LaunchedEffect(Unit) { + withContext(Dispatchers.IO) { + AppDatabase.getInstance(context).bookDao().getAll().collect { data -> + books.clear() + books.addAll(data) } } - Spacer(Modifier.padding(bottom = 40.dp)) + } + 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() diff --git a/app/src/main/java/com/example/myapplication/composeui/Enter.kt b/app/src/main/java/com/example/myapplication/composeui/Enter.kt index 81c2ad4..83ee5fb 100644 --- a/app/src/main/java/com/example/myapplication/composeui/Enter.kt +++ b/app/src/main/java/com/example/myapplication/composeui/Enter.kt @@ -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)) } } diff --git a/app/src/main/java/com/example/myapplication/composeui/ListAuthors.kt b/app/src/main/java/com/example/myapplication/composeui/ListAuthors.kt new file mode 100644 index 0000000..7565706 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/composeui/ListAuthors.kt @@ -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() } + 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() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/composeui/Profile.kt b/app/src/main/java/com/example/myapplication/composeui/Profile.kt index 3b1f6be..9b62b53 100644 --- a/app/src/main/java/com/example/myapplication/composeui/Profile.kt +++ b/app/src/main/java/com/example/myapplication/composeui/Profile.kt @@ -1,26 +1,255 @@ 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")) } + 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) @Composable @@ -29,7 +258,7 @@ fun ProfilePreview() { Surface( color = MaterialTheme.colorScheme.background ) { - Profile() + Profile(navController = null) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/composeui/Search.kt b/app/src/main/java/com/example/myapplication/composeui/Search.kt index 59b1279..b2e9617 100644 --- a/app/src/main/java/com/example/myapplication/composeui/Search.kt +++ b/app/src/main/java/com/example/myapplication/composeui/Search.kt @@ -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) } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/composeui/navigation/MainNavbar.kt b/app/src/main/java/com/example/myapplication/composeui/navigation/MainNavbar.kt index 8904e0d..66c2abf 100644 --- a/app/src/main/java/com/example/myapplication/composeui/navigation/MainNavbar.kt +++ b/app/src/main/java/com/example/myapplication/composeui/navigation/MainNavbar.kt @@ -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", "")) } } } } diff --git a/app/src/main/java/com/example/myapplication/composeui/navigation/Screen.kt b/app/src/main/java/com/example/myapplication/composeui/navigation/Screen.kt index 7b17e95..819dc81 100644 --- a/app/src/main/java/com/example/myapplication/composeui/navigation/Screen.kt +++ b/app/src/main/java/com/example/myapplication/composeui/navigation/Screen.kt @@ -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() diff --git a/app/src/main/java/com/example/myapplication/db/dao/AuthorDao.kt b/app/src/main/java/com/example/myapplication/db/dao/AuthorDao.kt new file mode 100644 index 0000000..ec9ef35 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/db/dao/AuthorDao.kt @@ -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> + + @Insert + suspend fun insert(author: Author) + + @Update + suspend fun update(author: Author) + + @Delete + suspend fun delete(author: Author) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/db/dao/BookDao.kt b/app/src/main/java/com/example/myapplication/db/dao/BookDao.kt new file mode 100644 index 0000000..b6eb7d6 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/db/dao/BookDao.kt @@ -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> + + @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> + + @Query("select * from books where books.user_id = :userId") + fun getByUserId(userId: Int): Flow> + + @Query("select * from books where books.author_id = :authorId") + fun getByAuthorId(authorId: Int): Flow> + + @Insert + suspend fun insert(book: Book) + + @Update + suspend fun update(book: Book) + + @Delete + suspend fun delete(book: Book) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/db/dao/UserDao.kt b/app/src/main/java/com/example/myapplication/db/dao/UserDao.kt new file mode 100644 index 0000000..b9eb2b3 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/db/dao/UserDao.kt @@ -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> + + @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) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/db/database/AppDatabase.kt b/app/src/main/java/com/example/myapplication/db/database/AppDatabase.kt new file mode 100644 index 0000000..1638411 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/db/database/AppDatabase.kt @@ -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 } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/db/model/Author.kt b/app/src/main/java/com/example/myapplication/db/model/Author.kt new file mode 100644 index 0000000..39834d2 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/db/model/Author.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/db/model/Book.kt b/app/src/main/java/com/example/myapplication/db/model/Book.kt new file mode 100644 index 0000000..21a871e --- /dev/null +++ b/app/src/main/java/com/example/myapplication/db/model/Book.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/db/model/BookWithAuthor.kt b/app/src/main/java/com/example/myapplication/db/model/BookWithAuthor.kt new file mode 100644 index 0000000..1161292 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/db/model/BookWithAuthor.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/db/model/User.kt b/app/src/main/java/com/example/myapplication/db/model/User.kt new file mode 100644 index 0000000..80eeb89 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/db/model/User.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/dogsheart.jpg b/app/src/main/res/drawable/dogsheart.jpg new file mode 100644 index 0000000..c7d7e5f Binary files /dev/null and b/app/src/main/res/drawable/dogsheart.jpg differ diff --git a/app/src/main/res/drawable/eggs.jpg b/app/src/main/res/drawable/eggs.jpg new file mode 100644 index 0000000..ceb8337 Binary files /dev/null and b/app/src/main/res/drawable/eggs.jpg differ diff --git a/app/src/main/res/drawable/or1984.jpg b/app/src/main/res/drawable/or1984.jpg new file mode 100644 index 0000000..2e79f19 Binary files /dev/null and b/app/src/main/res/drawable/or1984.jpg differ diff --git a/app/src/main/res/drawable/veld.jpg b/app/src/main/res/drawable/veld.jpg new file mode 100644 index 0000000..31824a9 Binary files /dev/null and b/app/src/main/res/drawable/veld.jpg differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index acb01fe..87f6cfa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,6 +9,7 @@ Поиск Загрузка Каталог + Писатели Книга Войти Создать аккаунт @@ -27,11 +28,13 @@ Название Автор + Содержание Содержание Добро пожаловать в мир книг! Найти Загрузить Добавить + Читать Вы можете помочь нашей библиотеке, загрузив свою книгу. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 9861456..3b1b951 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 } \ No newline at end of file diff --git a/Отчеты и макеты/Тюрнер_ЛР3.docx b/Отчеты и макеты/Тюрнер_ЛР3.docx new file mode 100644 index 0000000..c44848c Binary files /dev/null and b/Отчеты и макеты/Тюрнер_ЛР3.docx differ