diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 44ca2d9..54db5ea 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -37,5 +37,6 @@
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 934b019..09d332d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -73,4 +73,9 @@ dependencies {
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
+
+ // Paging
+ val paging_version = "3.2.0-rc01"
+ implementation("androidx.paging:paging-runtime:$paging_version")
+ implementation("androidx.paging:paging-compose:$paging_version")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e013846..36e417c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
Unit,
+ onUpdate: (AuthorDetails) -> Unit,
+) {
+ Column(
+ Modifier
+ .fillMaxWidth()
+ .padding(all = 10.dp)
+ .verticalScroll(ScrollState(1))
+ ) {
+ Spacer(modifier = Modifier.size(10.dp))
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = authorUiState.authorDetails.name,
+ onValueChange = { onUpdate(authorUiState.authorDetails.copy(name = it)) },
+ label = { Text("Имя автора") },
+ singleLine = true
+ )
+ Button(
+ onClick = onClick,
+ enabled = authorUiState.isEntryValid,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = "Сохранить")
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/BookCell.kt b/app/src/main/java/com/example/myapplication/composeui/BookCell.kt
new file mode 100644
index 0000000..7d7d968
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/BookCell.kt
@@ -0,0 +1,112 @@
+package com.example.myapplication.composeui
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Create
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import com.example.myapplication.composeui.ViewModel.BookListViewModel
+import com.example.myapplication.composeui.navigation.Screen
+import com.example.myapplication.db.model.Book
+import kotlinx.coroutines.launch
+
+@Composable
+fun BookCell(navController: NavController?, book: Book, viewModel: BookListViewModel = viewModel(factory = AppViewModelProvider.Factory)){
+ val scope = rememberCoroutineScope()
+ Column(modifier = Modifier
+ .padding(all = 5.dp)
+ .requiredSize(170.dp, 300.dp)
+ .clip(RoundedCornerShape(10.dp))
+ .border(1.dp, Color.LightGray, shape = RoundedCornerShape(10.dp))
+ .clickable(onClick = {
+ navController?.navigate(
+ Screen.BookView.route.replace(
+ "{id}",
+ book.uid.toString()
+ )
+ )
+ })
+ ) {
+ Button(modifier = Modifier
+ .fillMaxWidth(),
+ shape = RoundedCornerShape(0.dp),
+ contentPadding = PaddingValues(0.dp),
+ onClick = {
+ navController?.navigate(
+ Screen.BookView.route.replace(
+ "{id}",
+ book.uid.toString()
+ )
+ )
+ }) {
+ Image(
+ bitmap = book.cover!!.asImageBitmap(),
+ contentDescription = book.title,
+ contentScale = ContentScale.Crop,
+ modifier = Modifier
+ .requiredSize(170.dp)
+ .clip(RoundedCornerShape(0.dp))
+ )
+ }
+ Column(modifier = Modifier.padding(all=5.dp)) {
+ Text("${book.title}")
+ Row{
+ Button(
+ modifier = Modifier
+ .padding(all = 5.dp),
+ onClick = {
+ val route = Screen.BookEdit.route.replace("{id}", book.uid.toString())
+ navController!!.navigate(route)
+ },
+ ) {
+ Icon(
+ imageVector = Icons.Default.Create,
+ contentDescription = "Изменить",
+ tint = Color.White
+ )
+ }
+ Button(
+ modifier = Modifier
+ .padding(all = 5.dp),
+ onClick = {
+ scope.launch {
+ viewModel.deleteBook(book)
+ }
+ },
+ colors = ButtonDefaults.buttonColors(containerColor = Color.Red),
+ ) {
+ Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = "Удалить",
+ tint = Color.White
+ )
+ }
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/BookEdit.kt b/app/src/main/java/com/example/myapplication/composeui/BookEdit.kt
new file mode 100644
index 0000000..64bad9e
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/BookEdit.kt
@@ -0,0 +1,173 @@
+package com.example.myapplication.composeui
+
+import android.graphics.Bitmap
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.background
+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.text.KeyboardOptions
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Button
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import com.example.myapplication.R
+import com.example.myapplication.SingletonClass
+import com.example.myapplication.composeui.ViewModel.AuthorDropDownViewModel
+import com.example.myapplication.composeui.ViewModel.AuthorsListUiState
+import com.example.myapplication.composeui.ViewModel.AuthorsUiState
+import com.example.myapplication.composeui.ViewModel.BookDetails
+import com.example.myapplication.composeui.ViewModel.BookEditViewModel
+import com.example.myapplication.composeui.ViewModel.BookUiState
+import com.example.myapplication.composeui.ViewModel.ImageUploader
+import com.example.myapplication.db.model.Author
+import kotlinx.coroutines.launch
+
+@Composable
+fun BookEdit(navController: NavController, viewModel: BookEditViewModel = viewModel(factory = AppViewModelProvider.Factory), authorViewModel: AuthorDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory)){
+ val coroutineScope = rememberCoroutineScope()
+ authorViewModel.setCurrentAuthor(viewModel.bookUiState.bookDetails.authorId)
+ BookEdit(
+ bookUiState = viewModel.bookUiState,
+ authorUiState = authorViewModel.authorsUiState,
+ onClick = {
+ coroutineScope.launch {
+ viewModel.saveBook()
+ navController.popBackStack()
+ }
+ },
+ authorsListUiState = authorViewModel.authorsListUiState,
+ onUpdate = viewModel::updateUiState,
+ onAuthorUpdate = authorViewModel::updateUiState
+ )
+}
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun AuthorDropDown(
+ authorUiState: AuthorsUiState,
+ authorsListUiState: AuthorsListUiState,
+ onAuthorUpdate: (Author) -> Unit
+) {
+ var expanded: Boolean by remember { mutableStateOf(false) }
+ ExposedDropdownMenuBox(
+ modifier = Modifier
+ .padding(top = 7.dp),
+ expanded = expanded,
+ onExpandedChange = {
+ expanded = !expanded
+ }
+ ) {
+ TextField(
+ value =authorUiState.author?.name ?: stringResource(id = R.string.author_name),
+ onValueChange = {},
+ readOnly = true,
+ modifier = Modifier
+ .fillMaxWidth()
+ .menuAnchor()
+ )
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ modifier = Modifier
+ .background(Color.White)
+ .exposedDropdownSize()
+ ) {
+ authorsListUiState.authorList.forEach { author ->
+ DropdownMenuItem(
+ text = {
+ Text(text = author.name)
+ },
+ onClick = {
+ onAuthorUpdate(author)
+ expanded = false
+ }
+ )
+ }
+ }
+ }
+}
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun BookEdit(
+ bookUiState: BookUiState,
+ authorUiState: AuthorsUiState,
+ authorsListUiState: AuthorsListUiState,
+ onClick: () -> Unit,
+ onUpdate: (BookDetails) -> Unit,
+ onAuthorUpdate: (Author) -> Unit
+) {
+ fun handleImageUpload(bitmap: Bitmap) {
+ onUpdate(
+ bookUiState.bookDetails.copy(
+ cover = bitmap
+ )
+ )
+ }
+ var SingletonClass = SingletonClass()
+ onUpdate(bookUiState.bookDetails.copy(userId = SingletonClass.getUserId()))
+ Column(
+ Modifier
+ .fillMaxWidth()
+ .padding(all = 10.dp)
+ .verticalScroll(ScrollState(1))
+ ) {
+ Spacer(modifier = Modifier.size(10.dp))
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = bookUiState.bookDetails.title,
+ onValueChange = { onUpdate(bookUiState.bookDetails.copy(title = it)) },
+ label = { Text("Название книги") },
+ singleLine = true
+ )
+ AuthorDropDown(
+ authorUiState = authorUiState,
+ authorsListUiState = authorsListUiState,
+ onAuthorUpdate = {
+ onUpdate(bookUiState.bookDetails.copy(authorId = it.uid))
+ onAuthorUpdate(it)
+ }
+ )
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = bookUiState.bookDetails.description,
+ onValueChange = { onUpdate(bookUiState.bookDetails.copy(description = it)) },
+ label = { Text("Описание") },
+ )
+ ImageUploader(bitmap = bookUiState.bookDetails.cover, onResult = { bitmap: Bitmap -> handleImageUpload(bitmap)} )
+ OutlinedTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = bookUiState.bookDetails.content,
+ onValueChange = { onUpdate(bookUiState.bookDetails.copy(content = it)) },
+ label = { Text("Содержание") },
+ )
+ Button(
+ onClick = onClick,
+ enabled = bookUiState.isEntryValid,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(text = "Сохранить")
+ }
+
+ }
+}
\ 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
index 4e88681..dbec626 100644
--- a/app/src/main/java/com/example/myapplication/composeui/BookRead.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/BookRead.kt
@@ -14,9 +14,12 @@ 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.runtime.rememberCoroutineScope
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
+import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
+import com.example.myapplication.composeui.ViewModel.BookPageViewModel
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.BookWithAuthor
import com.example.myapplication.ui.theme.MyApplicationTheme
@@ -24,12 +27,12 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
-fun BookRead(id: Int) {
- val context = LocalContext.current
- val (bookWithAuthor, setBookWithAuthor) = remember { mutableStateOf(null) }
+fun BookRead(id: Int, viewModel: BookPageViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
+ val scope = rememberCoroutineScope()
+ val book = viewModel.bookPageUiState
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
- setBookWithAuthor(AppDatabase.getInstance(context).bookDao().getByUid(id))
+ viewModel.refreshState()
}
}
Column(
@@ -37,7 +40,7 @@ fun BookRead(id: Int) {
.fillMaxWidth()
.padding(all = 10.dp)
){
- Text(text = bookWithAuthor?.book?.content ?: "", fontSize=22.sp)
+ Text(text = book?.book?.content ?: "", fontSize=22.sp)
}
}
@@ -52,4 +55,4 @@ fun BookReadPreview() {
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
index 17dcf1b..052bcf0 100644
--- a/app/src/main/java/com/example/myapplication/composeui/BookSearch.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/BookSearch.kt
@@ -20,59 +20,36 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
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.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
+import androidx.navigation.compose.rememberNavController
+import androidx.paging.compose.LazyPagingItems
+import androidx.paging.compose.collectAsLazyPagingItems
import com.example.myapplication.R
+import com.example.myapplication.composeui.ViewModel.SearchPageViewModel
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.launch
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))
+fun BookSearch(navController: NavController, searchStr: String, viewModel: SearchPageViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
+ val coroutineScope = rememberCoroutineScope()
+ val searchPageUiState = viewModel.searchPageUiState
+ val searchBook: List = searchPageUiState.bookList
+ LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 180.dp)) {
+ items(searchBook.size) { index ->
+ BookCell(navController = navController, book = searchBook[index]!!.copy())
}
}
}
@@ -85,7 +62,7 @@ fun BookSearchPreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
- BookSearch(navController = null, "")
+ BookSearch(navController = rememberNavController(), "")
}
}
}
\ 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 450d194..b38b397 100644
--- a/app/src/main/java/com/example/myapplication/composeui/BookView.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/BookView.kt
@@ -1,76 +1,94 @@
package com.example.myapplication.composeui
+import android.content.Context
import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import androidx.compose.foundation.Image
+import androidx.compose.material3.Button
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.material3.Surface
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
+import androidx.compose.material3.OutlinedTextField
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.runtime.rememberCoroutineScope
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.graphics.asImageBitmap
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
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.composeui.ViewModel.BookPageViewModel
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.ui.theme.MyApplicationTheme
+private fun getBookImage(context: Context, imageId: String): Bitmap {
+ val inputStream = context.assets.open("${imageId}.jpg")
+ val bitmap = BitmapFactory.decodeStream(inputStream)
+ return bitmap
+}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun BookView(navController: NavController?, id: Int) {
- val context = LocalContext.current
- val (bookWithAuthor, setBookWithAuthor) = remember { mutableStateOf(null) }
+fun BookView(navController: NavController, id: Int, viewModel: BookPageViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
+ val scope = rememberCoroutineScope()
+ val book = viewModel.bookPageUiState
LaunchedEffect(Unit) {
- withContext(Dispatchers.IO) {
- setBookWithAuthor(AppDatabase.getInstance(context).bookDao().getByUid(id))
- }
+ viewModel.refreshState()
}
+ val context = LocalContext.current
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
+ .verticalScroll(rememberScrollState())
) {
- OutlinedTextField(modifier = Modifier.fillMaxWidth(),
- value = bookWithAuthor?.book?.title ?: "", onValueChange = {}, readOnly = true,
- label = {
- Text(stringResource(id = R.string.book_title))
- }
- )
- OutlinedTextField(modifier = Modifier.fillMaxWidth(),
- value = bookWithAuthor?.authorName ?: "", onValueChange = {}, readOnly = true,
- label = {
- Text(stringResource(id = R.string.author_name))
- }
- )
- OutlinedTextField(modifier = Modifier.fillMaxWidth(),
- value = bookWithAuthor?.book?.description ?: "", onValueChange = {}, readOnly = true,
- label = {
- Text(stringResource(id = R.string.description))
- }
- )
- Spacer(Modifier.padding(bottom = 10.dp))
- Button(
+ Image(
+ bitmap = book.book?.cover?.asImageBitmap() ?: getBookImage(context, "img").asImageBitmap(),
+ contentDescription = book.book?.title,
+ contentScale = ContentScale.FillBounds,
modifier = Modifier
+ .fillMaxSize()
+ .requiredSize(400.dp, 400.dp)
+ .padding(0.dp, 10.dp)
+ )
+ Column(
+ Modifier
.fillMaxWidth()
- .padding(all = 10.dp),
- onClick = { navController?.navigate(Screen.BookRead.route.replace("{id}", id.toString())) }) {
- Text(stringResource(id = R.string.read_book))
+ .padding(all = 10.dp)
+ ) {
+ OutlinedTextField(modifier = Modifier.fillMaxWidth(),
+ value = book.book?.title ?: "", onValueChange = {}, readOnly = true,
+ label = {
+ Text(stringResource(id = R.string.book_title))
+ }
+ )
+ OutlinedTextField(modifier = Modifier.fillMaxWidth(),
+ value = book.book?.description ?: "", onValueChange = {}, readOnly = true,
+ label = {
+ Text(stringResource(id = R.string.description))
+ }
+ )
+ 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))
+ }
}
}
}
@@ -83,7 +101,7 @@ fun BookViewPreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
- BookView(navController = null, id = 0)
+ BookView(navController = rememberNavController(), 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 0653b11..64cc8a8 100644
--- a/app/src/main/java/com/example/myapplication/composeui/Catalog.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/Catalog.kt
@@ -2,89 +2,70 @@ 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.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 androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
-import androidx.compose.foundation.lazy.grid.items
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.compose.rememberNavController
+import androidx.paging.compose.LazyPagingItems
+import androidx.paging.compose.collectAsLazyPagingItems
+import com.example.myapplication.composeui.ViewModel.BookListViewModel
import com.example.myapplication.composeui.navigation.Screen
+import com.example.myapplication.db.model.Book
import com.example.myapplication.ui.theme.MyApplicationTheme
@Composable
-fun Catalog(navController: NavController?) {
- 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)
- }
- }
- }
- 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) }
- )
- )
- }
- }
- }
+fun Catalog(navController: NavController, viewModel: BookListViewModel = viewModel(factory = AppViewModelProvider.Factory)){
+ val coroutineScope = rememberCoroutineScope()
+ val bookListUiState = viewModel.bookListUiState
+ val pagingBook: LazyPagingItems = viewModel.bookPagedData.collectAsLazyPagingItems()
+ Column() {
Button(
modifier = Modifier
.fillMaxWidth()
- .padding(horizontal = 10.dp),
- onClick = { navController?.navigate(Screen.Loader.route) }) {
- Text(stringResource(id = R.string.add_book))
+ .padding(all = 10.dp),
+ onClick = {
+ val route = Screen.BookEdit.route.replace("{id}", 0.toString())
+ navController.navigate(route)
+ }
+ ) {
+ Icon(
+ imageVector = Icons.Default.Add,
+ contentDescription = "Добавить",
+ )
+ Text("Добавить")
+ }
+ LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 180.dp)) {
+ items(pagingBook.itemCount) { index ->
+ BookCell(navController = navController, book = pagingBook[index]!!.copy())
+ }
}
}
}
-
-@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
-@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Preview(name = "Light Mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
-fun CatalogPreview() {
- MyApplicationTheme {
+private fun CatalogPreview(){
+ MyApplicationTheme{
Surface(
color = MaterialTheme.colorScheme.background
) {
- Catalog(navController = null)
+ Main(navController = rememberNavController())
}
}
}
\ No newline at end of file
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 83ee5fb..cbb66b4 100644
--- a/app/src/main/java/com/example/myapplication/composeui/Enter.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/Enter.kt
@@ -1,6 +1,7 @@
package com.example.myapplication.composeui
import android.content.res.Configuration
+import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -23,7 +24,11 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
+import androidx.navigation.compose.rememberNavController
+import com.example.myapplication.MainActivity
+import com.example.myapplication.SingletonClass
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.db.database.AppDatabase
import com.example.myapplication.db.model.User
@@ -35,16 +40,16 @@ import kotlinx.coroutines.GlobalScope
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun Enter(navController: NavController?) {
+fun Enter(navController: NavController) {
+ var SingletonClass = SingletonClass()
+ val scope = rememberCoroutineScope()
+ val context = LocalContext.current
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)) {
+ Column(
+ Modifier
+ .fillMaxWidth()
+ .padding(all = 40.dp)) {
Text(stringResource(id = R.string.login))
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
value = login, onValueChange = { login = it },
@@ -60,17 +65,15 @@ fun Enter(navController: NavController?) {
.fillMaxWidth()
.padding(all = 10.dp),
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 = "Неверный логин или пароль"
- }
+ scope.launch {if( AppDatabase.getInstance(context).userDao().tryLogin(login.toString(), password.toString()) != null){
+ SingletonClass.setUserId(AppDatabase.getInstance(context).userDao().tryLogin(login.toString(), password.toString())?.uid!!)
+ SingletonClass.setRole(AppDatabase.getInstance(context).userDao().tryLogin(login.toString(), password.toString())?.role!!)
+ navController?.navigate(Screen.Profile.route)
}
+ else{
+ val toast = Toast.makeText(MainActivity.appContext, "Неверный логин или пароль", Toast.LENGTH_SHORT)
+ toast.show()
+ }}
}) {
Text(stringResource(id = R.string.enter))
}
@@ -85,7 +88,7 @@ fun EnterPreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
- Enter(navController = null)
+ Enter(navController = rememberNavController())
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ListAuthors.kt b/app/src/main/java/com/example/myapplication/composeui/ListAuthors.kt
index 7565706..b5f7728 100644
--- a/app/src/main/java/com/example/myapplication/composeui/ListAuthors.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/ListAuthors.kt
@@ -2,68 +2,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.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
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.runtime.rememberCoroutineScope
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 androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import androidx.navigation.compose.rememberNavController
+import androidx.paging.compose.LazyPagingItems
+import androidx.paging.compose.collectAsLazyPagingItems
+import com.example.myapplication.composeui.ViewModel.AuthorListViewModel
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))
+fun ListAuthors(navController: NavController, viewModel: AuthorListViewModel = viewModel(factory = AppViewModelProvider.Factory)){
+ val coroutineScope = rememberCoroutineScope()
+ val authorListUiState = viewModel.authorListUiState
+ val pagingAuthor: LazyPagingItems = viewModel.authorPagedData.collectAsLazyPagingItems()
+ Column() {
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
- onClick = {}) {
- Text(stringResource(id = R.string.add_book))
+ onClick = {
+ val route = Screen.AuthorEdit.route.replace("{id}", 0.toString())
+ navController.navigate(route)
+ }
+ ) {
+ Icon(
+ imageVector = Icons.Default.Add,
+ contentDescription = "Добавить",
+ )
+ Text("Добавить")
+ }
+ LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 180.dp)) {
+ items(pagingAuthor.itemCount) { index ->
+ AuthorCell(navController = navController, author = pagingAuthor[index]!!.copy())
+ }
}
}
}
-
-@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
-@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Preview(name = "Light Mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
-fun ListAuthorsPreview() {
- MyApplicationTheme {
+private fun ListAuthorsPreview(){
+ MyApplicationTheme{
Surface(
color = MaterialTheme.colorScheme.background
) {
- ListAuthors()
+ Main(navController = rememberNavController())
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/Loader.kt b/app/src/main/java/com/example/myapplication/composeui/Loader.kt
deleted file mode 100644
index 254c3de..0000000
--- a/app/src/main/java/com/example/myapplication/composeui/Loader.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-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.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import com.example.myapplication.R
-import androidx.compose.foundation.BorderStroke
-import androidx.compose.material3.ButtonDefaults
-import com.example.myapplication.ui.theme.MyApplicationTheme
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun Loader() {
- Column(Modifier.fillMaxWidth().padding(all = 40.dp)) {
- Text(stringResource(id = R.string.help_me_pls))
- Spacer(Modifier.padding(bottom = 20.dp))
- val titleStr = remember{ mutableStateOf("") }
- Text(stringResource(id = R.string.book_title))
- OutlinedTextField(modifier = Modifier.fillMaxWidth(),
- value = titleStr.value, onValueChange = { newText -> titleStr.value = newText },
- )
- Spacer(Modifier.padding(bottom = 10.dp))
- val authorStr = remember{ mutableStateOf("") }
- Text(stringResource(id = R.string.author_name))
- OutlinedTextField(modifier = Modifier.fillMaxWidth(),
- value = authorStr.value, onValueChange = { newText -> authorStr.value = newText },
- )
- Spacer(Modifier.padding(bottom = 20.dp))
- Button(
- modifier = Modifier
- .fillMaxWidth()
- .padding(all = 10.dp),
- onClick = {}, colors = ButtonDefaults.buttonColors(containerColor = Color.LightGray, contentColor = Color.Black),
- border = BorderStroke(3.dp, Color.DarkGray)) {
- Text(stringResource(id = R.string.load_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 LoaderPreview() {
- MyApplicationTheme {
- Surface(
- color = MaterialTheme.colorScheme.background
- ) {
- Loader()
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/Main.kt b/app/src/main/java/com/example/myapplication/composeui/Main.kt
index 6396ec8..e7353d2 100644
--- a/app/src/main/java/com/example/myapplication/composeui/Main.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/Main.kt
@@ -25,9 +25,10 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.sp
import com.example.myapplication.R
import androidx.compose.ui.layout.ContentScale
+import androidx.navigation.compose.rememberNavController
@Composable
-fun Main(navController: NavController?) {
+fun Main(navController: NavController) {
Column(Modifier.padding(all = 40.dp)) {
Text(stringResource(id = R.string.welcome), modifier = Modifier.fillMaxWidth(), fontSize = 24.sp, textAlign = TextAlign.Center)
Spacer(Modifier.padding(bottom = 20.dp))
@@ -64,7 +65,7 @@ fun MainPreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
- Main(navController = null)
+ Main(navController = rememberNavController())
}
}
}
\ 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 9b62b53..216fa1e 100644
--- a/app/src/main/java/com/example/myapplication/composeui/Profile.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/Profile.kt
@@ -1,7 +1,10 @@
package com.example.myapplication.composeui
+import android.annotation.SuppressLint
import android.content.res.Configuration
import androidx.compose.foundation.BorderStroke
+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
@@ -29,223 +32,55 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
+import androidx.navigation.compose.rememberNavController
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.SingletonClass
+import com.example.myapplication.composeui.ViewModel.UserPageViewModel
import com.example.myapplication.ui.theme.MyApplicationTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
-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(
+fun Profile(navController: NavController) {
+ var SingletonClass = SingletonClass()
+ Column(Modifier.padding(all = 40.dp)) {
+ Image(
+ painter = painterResource(id = R.drawable.user),
+ contentDescription = "logo",
+ contentScale = ContentScale.FillWidth,
+ modifier = Modifier.fillMaxWidth()
+ .clickable(
+ enabled = true,
onClick = {
- openDialogEdit.value = false
- /* TODO */
+ navController?.navigate(
+ Screen.ProfileEdit.route.replace(
+ "{id}",
+ SingletonClass.getUserId().toString()
+ )
+ )
}
- ) {
- 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("Нет")
- }
- }
+ )
)
+ Spacer (Modifier.padding(bottom = 10.dp))
+ Text("Открыть профиль", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
}
}
@@ -258,7 +93,7 @@ fun ProfilePreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
- Profile(navController = null)
+ Profile(navController = rememberNavController())
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ProfileEdit.kt b/app/src/main/java/com/example/myapplication/composeui/ProfileEdit.kt
new file mode 100644
index 0000000..da17121
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ProfileEdit.kt
@@ -0,0 +1,115 @@
+package com.example.myapplication.composeui
+
+import android.annotation.SuppressLint
+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.foundation.lazy.LazyColumn
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+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.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.rememberCoroutineScope
+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.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.navigation.NavController
+import androidx.navigation.compose.rememberNavController
+import com.example.myapplication.R
+import com.example.myapplication.composeui.ViewModel.UserDetails
+import com.example.myapplication.composeui.ViewModel.UserEditViewModel
+import com.example.myapplication.composeui.ViewModel.UserPageViewModel
+import com.example.myapplication.composeui.ViewModel.UserUiState
+import com.example.myapplication.composeui.navigation.Screen
+import com.example.myapplication.ui.theme.MyApplicationTheme
+import kotlinx.coroutines.launch
+
+@Composable
+fun ProfileEdit(navController: NavController, viewModel: UserEditViewModel = viewModel(factory = AppViewModelProvider.Factory)){
+ val scope = rememberCoroutineScope()
+ viewModel.userUiState.userDetails.login.isBlank()
+ ProfileEdit(
+ navController = navController,
+ userUiState = viewModel.userUiState,
+ onClick = {
+ scope.launch {
+ viewModel.saveUser()
+ navController.popBackStack()
+ }
+ },
+ onUpdate = viewModel::updateUiState
+ )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun ProfileEdit(
+ navController: NavController,
+ userUiState: UserUiState,
+ onClick: () -> Unit,
+ onUpdate: (UserDetails) -> Unit
+){
+ LazyColumn() {
+ item {
+ OutlinedTextField(
+ value = userUiState.userDetails.email,
+ onValueChange = { onUpdate(userUiState.userDetails.copy(email = it)) },
+ singleLine = true,
+ label = { Text("Почта", fontSize = 20.sp) },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(10.dp, 5.dp)
+ )
+ OutlinedTextField(
+ value = userUiState.userDetails.login,
+ onValueChange = { onUpdate(userUiState.userDetails.copy(login = it)) },
+ label = { Text("Логин", fontSize = 20.sp) },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(10.dp, 5.dp)
+ )
+ OutlinedTextField(
+ value = userUiState.userDetails.password,
+ onValueChange = { onUpdate(userUiState.userDetails.copy(password = it)) },
+ singleLine = true,
+ label = { Text("Пароль", fontSize = 20.sp) },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(10.dp, 5.dp)
+ )
+ Button(
+ onClick = onClick,
+ enabled = userUiState.isEntryValid,
+ modifier = Modifier.fillMaxWidth()
+ .padding(10.dp, 0.dp),
+ ) {
+ Text("Изменить")
+ }
+ Button(
+ onClick = { navController.popBackStack() },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(10.dp, 0.dp),
+ colors = ButtonDefaults.buttonColors(containerColor = Color.LightGray, contentColor = Color.Black))
+ {
+ Text("Назад")
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/Registration.kt b/app/src/main/java/com/example/myapplication/composeui/Registration.kt
index c8ac77b..30d693e 100644
--- a/app/src/main/java/com/example/myapplication/composeui/Registration.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/Registration.kt
@@ -18,38 +18,53 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.platform.LocalContext
+import androidx.navigation.compose.rememberNavController
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.ui.theme.MyApplicationTheme
import com.example.myapplication.R
+import com.example.myapplication.db.database.AppDatabase
+import com.example.myapplication.db.model.User
+import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun Registration(navController: NavController?) {
+fun Registration(navController: NavController) {
+ val scope = rememberCoroutineScope()
+ val context = LocalContext.current
Column(Modifier.fillMaxWidth().padding(all = 40.dp)) {
- val mailStr = remember{mutableStateOf("")}
+ var mail by remember{mutableStateOf("")}
Text(stringResource(id = R.string.email))
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
- value = mailStr.value, onValueChange = { newText -> mailStr.value = newText },
+ value = mail, onValueChange = { mail = it },
)
Spacer(Modifier.padding(bottom = 10.dp))
- val loginStr = remember{mutableStateOf("")}
+ var login by 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("")}
+ var password by 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.Enter.route) }) {
+ onClick = {
+ scope.launch{
+ //AppDatabase.getInstance(context).userDao().insert(User(0, login.toString(), password.toString(), mail.toString(), "USER"))
+ }
+ navController?.navigate(Screen.Enter.route)
+ }) {
Text(stringResource(id = R.string.create_acc))
}
}
@@ -63,7 +78,7 @@ fun RegistrationPreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
- Registration(navController = null)
+ Registration(navController = rememberNavController())
}
}
}
\ 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 b2e9617..1281b7a 100644
--- a/app/src/main/java/com/example/myapplication/composeui/Search.kt
+++ b/app/src/main/java/com/example/myapplication/composeui/Search.kt
@@ -27,6 +27,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
+import androidx.navigation.compose.rememberNavController
import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.R
import com.example.myapplication.ui.theme.MyApplicationTheme
@@ -39,7 +40,10 @@ fun Search(navController: NavController?) {
Modifier
.fillMaxWidth()
.padding(all = 40.dp)) {
- Column(Modifier.background(color = colorResource(R.color.green_200)).padding(all = 20.dp)) {
+ Column(
+ Modifier
+ .background(color = colorResource(R.color.green_200))
+ .padding(all = 20.dp)) {
Text(stringResource(id = R.string.welcome), color = Color.White)
Text(stringResource(id = R.string.book_world), color = Color.White)
}
@@ -57,7 +61,7 @@ fun Search(navController: NavController?) {
if(!searchStr.isEmpty()){
navController?.navigate(
Screen.BookSearch.route.replace(
- "{text}",
+ "{searchStr}",
searchStr
)
)
@@ -76,7 +80,7 @@ fun SearchPreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
- Search(navController=null)
+ Search(navController = rememberNavController())
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/AuthorDropDownViewModel.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/AuthorDropDownViewModel.kt
new file mode 100644
index 0000000..007addc
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/AuthorDropDownViewModel.kt
@@ -0,0 +1,46 @@
+package com.example.myapplication.composeui.ViewModel
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.db.model.Author
+import kotlinx.coroutines.launch
+import com.example.myapplication.db.respository.AuthorRepository
+
+class AuthorDropDownViewModel(
+ private val authorRepository: AuthorRepository
+) : ViewModel() {
+ var authorsListUiState by mutableStateOf(AuthorsListUiState())
+ private set
+
+ var authorsUiState by mutableStateOf(AuthorsUiState())
+ private set
+
+ init {
+ viewModelScope.launch {
+ authorsListUiState = AuthorsListUiState(authorRepository.getAllDrop())
+ }
+ }
+
+ fun setCurrentAuthor(authorId: Int) {
+ val author: Author? =
+ authorsListUiState.authorList.firstOrNull { author -> author.uid == authorId }
+ author?.let { updateUiState(it) }
+ }
+
+ fun updateUiState(author: Author) {
+ authorsUiState = AuthorsUiState(
+ author = author
+ )
+ }
+}
+
+data class AuthorsListUiState(val authorList: List = listOf())
+
+data class AuthorsUiState(
+ val author: Author? = null
+)
+
+fun Author.toUiState() = AuthorsUiState(author = Author(uid = uid, name = name))
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/AuthorEditViewModel.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/AuthorEditViewModel.kt
new file mode 100644
index 0000000..60e63b2
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/AuthorEditViewModel.kt
@@ -0,0 +1,71 @@
+package com.example.myapplication.composeui.ViewModel
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.db.model.Author
+import com.example.myapplication.db.respository.AuthorRepository
+import kotlinx.coroutines.launch
+
+class AuthorEditViewModel(savedStateHandle: SavedStateHandle,
+ private val authorRepository: AuthorRepository
+) : ViewModel(){
+ var authorUiState by mutableStateOf(AuthorUiState())
+ private set
+
+ private val authorUid: Int = checkNotNull(savedStateHandle["id"])
+ init {
+ viewModelScope.launch {
+ if (authorUid > 0) {
+ authorUiState = authorRepository.getByUid(authorUid)
+ .toUiState(true)
+ }
+ }
+ }
+
+ fun updateUiState(authorDetails: AuthorDetails) {
+ authorUiState = AuthorUiState(
+ authorDetails = authorDetails,
+ isEntryValid = validateInput(authorDetails)
+ )
+ }
+
+ suspend fun saveAuthor() {
+ if (validateInput()) {
+ if (authorUid > 0) {
+ authorRepository.update(authorUiState.authorDetails.toAuthor(authorUid))
+ } else {
+ authorRepository.insert(authorUiState.authorDetails.toAuthor())
+ }
+ }
+ }
+
+ private fun validateInput(uiState: AuthorDetails = authorUiState.authorDetails): Boolean {
+ return with(uiState) {
+ name.isNotBlank()
+ }
+ }
+}
+data class AuthorUiState(
+ val authorDetails: AuthorDetails = AuthorDetails(),
+ val isEntryValid: Boolean = false
+)
+data class AuthorDetails(
+ val name: String = ""
+)
+fun AuthorDetails.toAuthor(uid: Int = 0): Author = Author(
+ uid = uid,
+ name = name
+)
+
+fun Author.toDetails(): AuthorDetails = AuthorDetails(
+ name = name
+)
+
+fun Author.toUiState(isEntryValid: Boolean = false): AuthorUiState = AuthorUiState(
+ authorDetails = this.toDetails(),
+ isEntryValid = isEntryValid
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/AuthorListViewModel.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/AuthorListViewModel.kt
new file mode 100644
index 0000000..9dab526
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/AuthorListViewModel.kt
@@ -0,0 +1,32 @@
+package com.example.myapplication.composeui.ViewModel
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import com.example.myapplication.db.model.Author
+import com.example.myapplication.db.respository.AuthorRepository
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+class AuthorListViewModel(
+ private val authorRepository: AuthorRepository
+) : ViewModel(){
+ var authorListUiState by mutableStateOf(AuthorListUiState())
+ private set
+ init {
+ viewModelScope.launch {
+ refreshState()
+ }
+ }
+ suspend fun refreshState() {
+ authorListUiState = AuthorListUiState(authorRepository.getAll())
+ }
+ val authorPagedData: Flow> = authorRepository.loadAllAuthorsPaged()
+ suspend fun deleteAuthor(author: Author) {
+ authorRepository.delete(author)
+ }
+}
+data class AuthorListUiState(val authorList: List = listOf())
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/BookEditViewModel.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/BookEditViewModel.kt
new file mode 100644
index 0000000..8401d76
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/BookEditViewModel.kt
@@ -0,0 +1,91 @@
+package com.example.myapplication.composeui.ViewModel
+
+import android.graphics.Bitmap
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import com.example.myapplication.db.respository.BookRepository
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.db.model.Book
+import kotlinx.coroutines.launch
+
+class BookEditViewModel(savedStateHandle: SavedStateHandle,
+ private val bookRepository: BookRepository) : ViewModel(){
+ var bookUiState by mutableStateOf(BookUiState())
+ private set
+
+ private val bookUid: Int = checkNotNull(savedStateHandle["id"])
+ init {
+ viewModelScope.launch {
+ if (bookUid > 0) {
+ bookUiState = bookRepository.getByUid(bookUid)
+ .toUiState(true)
+ }
+ }
+ }
+
+ fun updateUiState(bookDetails: BookDetails) {
+ bookUiState = BookUiState(
+ bookDetails = bookDetails,
+ isEntryValid = validateInput(bookDetails)
+ )
+ }
+
+ suspend fun saveBook() {
+ if (validateInput()) {
+ if (bookUid > 0) {
+ bookRepository.update(bookUiState.bookDetails.toBook(bookUid))
+ } else {
+ bookRepository.insert(bookUiState.bookDetails.toBook())
+ }
+ }
+ }
+
+ private fun validateInput(uiState: BookDetails = bookUiState.bookDetails): Boolean {
+ return with(uiState) {
+ title.isNotBlank()
+ && description.isNotBlank()
+ && content.isNotBlank()
+ && cover != null
+ && authorId >= 0
+ && userId >= 0
+ }
+ }
+}
+data class BookUiState(
+ val bookDetails: BookDetails = BookDetails(),
+ val isEntryValid: Boolean = false
+)
+data class BookDetails(
+ val title: String = "",
+ val description: String = "",
+ val content: String = "",
+ val cover: Bitmap? = null,
+ val authorId: Int = 0,
+ val userId: Int = 0,
+)
+fun BookDetails.toBook(uid: Int = 0): Book = Book(
+ uid = uid,
+ title = title,
+ description = description,
+ content = content,
+ cover = cover,
+ authorId = authorId,
+ userId = userId
+)
+
+fun Book.toDetails(): BookDetails = BookDetails(
+ title = title,
+ description = description,
+ content = content,
+ cover = cover,
+ authorId = authorId,
+ userId = userId
+)
+
+fun Book.toUiState(isEntryValid: Boolean = false): BookUiState = BookUiState(
+ bookDetails = this.toDetails(),
+ isEntryValid = isEntryValid
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/BookListViewModel.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/BookListViewModel.kt
new file mode 100644
index 0000000..2d1e95b
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/BookListViewModel.kt
@@ -0,0 +1,33 @@
+package com.example.myapplication.composeui.ViewModel
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import com.example.myapplication.db.model.Book
+import com.example.myapplication.db.respository.BookRepository
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.Flow
+
+class BookListViewModel(
+ private val bookRepository: BookRepository
+) : ViewModel() {
+ var bookListUiState by mutableStateOf(BookListUiState())
+ private set
+ init {
+ viewModelScope.launch {
+ refreshState()
+ }
+ }
+ suspend fun refreshState() {
+ bookListUiState = BookListUiState(bookRepository.getAll())
+ }
+ val bookPagedData: Flow> = bookRepository.loadAllBooksPaged()
+ suspend fun deleteBook(book: Book) {
+ bookRepository.delete(book)
+ }
+}
+
+data class BookListUiState(val bookList: List = listOf())
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/BookPageViewModel.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/BookPageViewModel.kt
new file mode 100644
index 0000000..412078b
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/BookPageViewModel.kt
@@ -0,0 +1,33 @@
+package com.example.myapplication.composeui.ViewModel
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.SavedStateHandle
+import com.example.myapplication.db.model.Book
+import com.example.myapplication.db.model.BookWithAuthor
+import com.example.myapplication.db.respository.BookRepository
+import kotlinx.coroutines.launch
+
+class BookPageViewModel(private val bookRespository: BookRepository, savedStateHandle: SavedStateHandle) : ViewModel(){
+ private val bookId: Int = checkNotNull(savedStateHandle["id"])
+ var bookPageUiState by mutableStateOf(BookPageUiState())
+ private set
+ init {
+ viewModelScope.launch {
+ if (bookId > 0) {
+ refreshState()
+ }
+ }
+ }
+ suspend fun refreshState() {
+ bookPageUiState = BookPageUiState(bookRespository.getByUid(bookId))
+ }
+ suspend fun deleteBook(book: Book) {
+ bookRespository.delete(book)
+ }
+}
+
+data class BookPageUiState(val book: Book? = null)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/ImageUploader.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/ImageUploader.kt
new file mode 100644
index 0000000..9216217
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/ImageUploader.kt
@@ -0,0 +1,126 @@
+package com.example.myapplication.composeui.ViewModel
+
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.net.Uri
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.example.myapplication.R
+
+@Composable
+fun ImageUploader(
+ bitmap: Bitmap?,
+ onResult: (Bitmap) -> Unit
+) {
+ val context = LocalContext.current
+ val title: String = if (bitmap == null) {
+ "Не загружено"
+ } else {
+ "Размер: " + bitmap.width + " " + bitmap.height
+ }
+
+ val launcher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.GetContent(),
+ onResult = { uri: Uri? ->
+ uri?.let {
+ val inputStream = context.contentResolver.openInputStream(uri)
+ val newBitmap: Bitmap = BitmapFactory.decodeStream(inputStream)
+ val scaledBitmap = resizeBitmapWithAspectRatio(newBitmap, 200)
+ onResult(scaledBitmap)
+ }
+ }
+ )
+
+ Row(
+ modifier = Modifier.height(IntrinsicSize.Min)
+ ) {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier
+ .background(
+ color = MaterialTheme.colorScheme.surface,
+ shape = RoundedCornerShape(10.dp)
+ )
+ .weight(0.25F)
+ .aspectRatio(1F)
+ ) {
+ if (bitmap != null) {
+ Image(
+ bitmap = bitmap.asImageBitmap(),
+ contentDescription = null,
+ modifier = Modifier.width(100.dp)
+ )
+ } else {
+ Icon(
+ painter = painterResource(R.drawable.ic_camera),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onBackground
+ )
+ }
+ }
+ Spacer(modifier = Modifier.width(10.dp))
+ Column(
+ modifier = Modifier
+ .background(
+ color = MaterialTheme.colorScheme.surface,
+ shape = RoundedCornerShape(10.dp)
+ )
+ .fillMaxHeight()
+ .padding(10.dp)
+ .weight(0.75F)
+ ) {
+ Text(
+ text = title,
+ color = MaterialTheme.colorScheme.onBackground,
+ style = MaterialTheme.typography.labelMedium
+ )
+ Spacer(modifier = Modifier.weight(1F))
+ Button(
+ onClick = { launcher.launch("image/*") },
+ ){
+ Text(text = "+")
+ }
+ }
+ }
+}
+fun resizeBitmapWithAspectRatio(bitmap: Bitmap, maxHeight: Int): Bitmap {
+ if (bitmap.height <= maxHeight) {
+ return bitmap
+ }
+
+ val aspectRatio = bitmap.width.toFloat() / bitmap.height
+ val newWidth = (maxHeight * aspectRatio).toInt()
+
+ return Bitmap.createScaledBitmap(bitmap, newWidth, maxHeight, true)
+}
+@Preview(name = "Light Mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun CuteImageLoaderPreview() {
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/SearchPageViewModel.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/SearchPageViewModel.kt
new file mode 100644
index 0000000..09e3d19
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/SearchPageViewModel.kt
@@ -0,0 +1,31 @@
+package com.example.myapplication.composeui.ViewModel
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.paging.PagingData
+import com.example.myapplication.db.model.Book
+import com.example.myapplication.db.respository.BookRepository
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.Flow
+
+class SearchPageViewModel(private val bookRepository: BookRepository, savedStateHandle: SavedStateHandle) : ViewModel(){
+ private val searchStr: String = checkNotNull(savedStateHandle["searchStr"])
+ var searchPageUiState by mutableStateOf(SearchPageUiState())
+ private set
+ init {
+ viewModelScope.launch {
+ refreshState()
+ }
+ }
+ suspend fun refreshState() {
+ searchPageUiState = SearchPageUiState(bookRepository.getBySearch(searchStr))
+ }
+
+ val bookPagedData: Flow> = bookRepository.loadAllBooksPaged()
+}
+
+data class SearchPageUiState(val bookList: List = listOf())
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/UserEditViewModel.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/UserEditViewModel.kt
new file mode 100644
index 0000000..7ddbe2b
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/UserEditViewModel.kt
@@ -0,0 +1,84 @@
+package com.example.myapplication.composeui.ViewModel
+
+import android.graphics.Bitmap
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import com.example.myapplication.db.respository.UserRepository
+import androidx.lifecycle.viewModelScope
+import com.example.myapplication.db.model.User
+import kotlinx.coroutines.launch
+
+class UserEditViewModel(savedStateHandle: SavedStateHandle,
+ private val userRepository: UserRepository
+) : ViewModel(){
+ var userUiState by mutableStateOf(UserUiState())
+ private set
+
+ private val userUid: Int = checkNotNull(savedStateHandle["id"])
+ init {
+ viewModelScope.launch {
+ if (userUid > 0) {
+ userUiState = userRepository.getByUid(userUid)
+ .toUiState(true)
+ }
+ }
+ }
+
+ fun updateUiState(userDetails: UserDetails) {
+ userUiState = UserUiState(
+ userDetails = userDetails,
+ isEntryValid = validateInput(userDetails)
+ )
+ }
+
+ suspend fun saveUser() {
+ if (validateInput()) {
+ if (userUid > 0) {
+ userRepository.update(userUiState.userDetails.toUser(userUid))
+ } else {
+ userRepository.insert(userUiState.userDetails.toUser())
+ }
+ }
+ }
+
+ private fun validateInput(uiState: UserDetails = userUiState.userDetails): Boolean {
+ return with(uiState) {
+ login.isNotBlank()
+ && password.isNotBlank()
+ && email.isNotBlank()
+ && role.isNotBlank()
+ }
+ }
+}
+data class UserUiState(
+ val userDetails: UserDetails = UserDetails(),
+ val isEntryValid: Boolean = false
+)
+data class UserDetails(
+ val login: String = "",
+ val password: String = "",
+ val email: String = "",
+ val role: String = ""
+)
+fun UserDetails.toUser(uid: Int = 0): User = User(
+ uid = uid,
+ login = login,
+ password = password,
+ email = email,
+ role = role
+)
+
+fun User.toDetails(): UserDetails = UserDetails(
+ login = login,
+ password = password,
+ email = email,
+ role = role
+)
+
+fun User.toUiState(isEntryValid: Boolean = false): UserUiState = UserUiState(
+ userDetails = this.toDetails(),
+ isEntryValid = isEntryValid
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/composeui/ViewModel/UserPageViewModel.kt b/app/src/main/java/com/example/myapplication/composeui/ViewModel/UserPageViewModel.kt
new file mode 100644
index 0000000..796da85
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/composeui/ViewModel/UserPageViewModel.kt
@@ -0,0 +1,31 @@
+package com.example.myapplication.composeui.ViewModel
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.SavedStateHandle
+import com.example.myapplication.db.model.User
+import kotlinx.coroutines.launch
+import com.example.myapplication.db.respository.UserRepository
+
+
+class UserPageViewModel(savedStateHandle: SavedStateHandle, private val userRepository: UserRepository) : ViewModel(){
+ private val userId: Int = checkNotNull(savedStateHandle["id"])
+ var userPageUiState by mutableStateOf(UserPageUiState())
+ private set
+ init {
+ viewModelScope.launch {
+ if (userId > 0) {
+ refreshState()
+ }
+ }
+ }
+ suspend fun refreshState() {
+ userPageUiState = UserPageUiState(userRepository.getByUid(userId))
+ }
+
+}
+
+data class UserPageUiState(val user: User? = 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 66c2abf..dfddcb0 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
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.foundation.layout.Box
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -19,8 +20,10 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.shadow
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
@@ -37,13 +40,15 @@ import com.example.myapplication.composeui.Main
import com.example.myapplication.composeui.Registration
import com.example.myapplication.composeui.Enter
import com.example.myapplication.composeui.Profile
+import com.example.myapplication.composeui.ProfileEdit
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.BookEdit
import com.example.myapplication.composeui.BookSearch
import com.example.myapplication.composeui.ListAuthors
+import com.example.myapplication.composeui.AuthorEdit
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -51,29 +56,31 @@ fun Topbar(
navController: NavHostController,
currentScreen: Screen?
) {
- TopAppBar(
- colors = TopAppBarDefaults.smallTopAppBarColors(
- containerColor = MaterialTheme.colorScheme.primary,
- titleContentColor = MaterialTheme.colorScheme.onPrimary,
- ),
- title = {
- Text(stringResource(currentScreen?.resourceId ?: R.string.app_name))
- },
- navigationIcon = {
- if (
- navController.previousBackStackEntry != null
- && (currentScreen == null || !currentScreen.showInBottomBar)
- ) {
- IconButton(onClick = { navController.navigateUp() }) {
- Icon(
- imageVector = Icons.Filled.ArrowBack,
- contentDescription = null,
- tint = MaterialTheme.colorScheme.onPrimary
- )
+ Box(modifier = Modifier.shadow(1.dp)) {
+ TopAppBar(
+ colors = TopAppBarDefaults.smallTopAppBarColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ titleContentColor = MaterialTheme.colorScheme.onPrimary,
+ ),
+ title = {
+ Text(stringResource(currentScreen?.resourceId ?: R.string.app_name))
+ },
+ navigationIcon = {
+ if (
+ navController.previousBackStackEntry != null
+ && (currentScreen == null || !currentScreen.showInBottomBar)
+ ) {
+ IconButton(onClick = { navController.navigateUp() }) {
+ Icon(
+ imageVector = Icons.Filled.ArrowBack,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onPrimary
+ )
+ }
}
}
- }
- )
+ )
+ }
}
@Composable
@@ -117,10 +124,14 @@ fun Navhost(
composable(Screen.Registration.route) { Registration(navController) }
composable(Screen.Enter.route) { Enter(navController) }
composable(Screen.Profile.route) { Profile(navController) }
+ composable(
+ Screen.ProfileEdit.route,
+ arguments = listOf(navArgument("id") { type = NavType.IntType })
+ ) { backStackEntry ->
+ backStackEntry.arguments?.let { ProfileEdit(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 })
@@ -134,10 +145,23 @@ fun Navhost(
backStackEntry.arguments?.let { BookRead(it.getInt("id")) }
}
composable(
- Screen.BookSearch.route,
- arguments = listOf(navArgument("text") { type = NavType.StringType })
+ Screen.BookEdit.route,
+ arguments = listOf(navArgument("id") { type = NavType.IntType })
) { backStackEntry ->
- backStackEntry.arguments?.let { BookSearch(navController = navController ,it.getString ("text", "")) }
+ backStackEntry.arguments?.let { BookEdit(navController) }
+ }
+ composable(Screen.ListAuthors.route) { ListAuthors(navController) }
+ composable(
+ Screen.AuthorEdit.route,
+ arguments = listOf(navArgument("id") { type = NavType.IntType })
+ ) { backStackEntry ->
+ backStackEntry.arguments?.let { AuthorEdit(navController) }
+ }
+ composable(
+ Screen.BookSearch.route,
+ arguments = listOf(navArgument("searchStr") { type = NavType.StringType })
+ ) { backStackEntry ->
+ backStackEntry.arguments?.let { BookSearch(navController = navController ,it.getString ("searchStr", "")) }
}
}
}
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 819dc81..3d75d0f 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
@@ -28,6 +28,9 @@ enum class Screen(
Profile(
"profile", R.string.profile, Icons.Filled.AccountCircle
),
+ ProfileEdit(
+ "profile-edit/{id}", R.string.profile, showInBottomBar = false
+ ),
Search(
"search", R.string.search, Icons.Filled.Search
),
@@ -37,15 +40,18 @@ enum class Screen(
ListAuthors(
"listauthors", R.string.listauthors, Icons.Filled.Face
),
- Loader(
- "loader", R.string.loader, showInBottomBar = false
+ AuthorEdit(
+ "author-edit/{id}", R.string.book_view_title, showInBottomBar = false
),
BookSearch(
- "book-search/{text}", R.string.search, showInBottomBar = false
+ "book-search/{searchStr}", R.string.search, showInBottomBar = false
),
BookView(
"book-view/{id}", R.string.book_view_title, showInBottomBar = false
),
+ BookEdit(
+ "book-edit/{id}", R.string.book_view_title, showInBottomBar = false
+ ),
BookRead(
"book-read/{id}", R.string.book_view_title, showInBottomBar = false
);
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
index ec9ef35..85c0748 100644
--- a/app/src/main/java/com/example/myapplication/db/dao/AuthorDao.kt
+++ b/app/src/main/java/com/example/myapplication/db/dao/AuthorDao.kt
@@ -1,18 +1,24 @@
package com.example.myapplication.db.dao
+import androidx.paging.PagingSource
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 com.example.myapplication.db.model.Author
import kotlinx.coroutines.flow.Flow
@Dao
interface AuthorDao {
@Query("select * from authors order by author_name collate nocase asc")
- fun getAll(): Flow>
+ suspend fun getAll(): List
+ @Query("select * from authors order by author_name collate nocase asc")
+ suspend fun getAllDrop(): List
+ @Query("select * from authors where authors.uid = :uid")
+ suspend fun getByUid(uid: Int): Author
+ @Query("select * from authors order by author_name collate nocase asc")
+ fun loadAllAuthorsPaged(): PagingSource
@Insert
suspend fun insert(author: Author)
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
index b6eb7d6..301239d 100644
--- a/app/src/main/java/com/example/myapplication/db/dao/BookDao.kt
+++ b/app/src/main/java/com/example/myapplication/db/dao/BookDao.kt
@@ -1,5 +1,6 @@
package com.example.myapplication.db.dao
+import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
@@ -12,13 +13,13 @@ import com.example.myapplication.db.model.BookWithAuthor;
@Dao
interface BookDao {
@Query("select * from books order by title collate nocase asc")
- fun getAll(): Flow>
+ suspend fun getAll(): List
- @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.uid = :uid")
+ suspend fun getByUid(uid: Int): Book
@Query("select * from books where books.title LIKE '%' || :searchStr || '%'")
- fun getBySearch(searchStr: String): Flow>
+ suspend fun getBySearch(searchStr: String): List
@Query("select * from books where books.user_id = :userId")
fun getByUserId(userId: Int): Flow>
@@ -26,6 +27,9 @@ interface BookDao {
@Query("select * from books where books.author_id = :authorId")
fun getByAuthorId(authorId: Int): Flow>
+ @Query("select * from books order by title collate nocase asc")
+ fun loadAllBooksPaged(): PagingSource
+
@Insert
suspend fun insert(book: Book)
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
index b9eb2b3..3d59968 100644
--- a/app/src/main/java/com/example/myapplication/db/dao/UserDao.kt
+++ b/app/src/main/java/com/example/myapplication/db/dao/UserDao.kt
@@ -7,18 +7,22 @@ import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow
import com.example.myapplication.db.model.User;
+import com.example.myapplication.db.model.Book;
@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
+ @Query("select * from books where books.user_id = :userid")
+ fun getUserBooks(userid: Int): Flow>
+
+ @Query("select * from users where login = :login and password = :password")
+ suspend fun tryLogin(login: String, password: String): User?
+
@Insert
suspend fun insert(user: User)
diff --git a/app/src/main/java/com/example/myapplication/db/database/AppContainer.kt b/app/src/main/java/com/example/myapplication/db/database/AppContainer.kt
new file mode 100644
index 0000000..2613878
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/db/database/AppContainer.kt
@@ -0,0 +1,38 @@
+package com.example.myapplication.db.database
+
+import android.content.Context
+import com.example.myapplication.db.dao.BookDao
+import com.example.myapplication.db.dao.AuthorDao
+import com.example.myapplication.db.dao.UserDao
+import com.example.myapplication.db.respository.BookRepository
+import com.example.myapplication.db.respository.AuthorRepository
+import com.example.myapplication.db.respository.UserRepository
+import com.example.myapplication.db.respository.OfflineBookRepository
+import com.example.myapplication.db.respository.OfflineAuthorRepository
+import com.example.myapplication.db.respository.OfflineUserRepository
+
+interface AppContainer {
+ val bookRepository: BookRepository
+ val authorRepository: AuthorRepository
+ val userRepository: UserRepository
+ companion object {
+ const val TIMEOUT = 5000L
+ const val LIMIT = 10
+ }
+}
+
+class AppDataContainer(private val context: Context) : AppContainer {
+ override val bookRepository: BookRepository by lazy {
+ OfflineBookRepository(AppDatabase.getInstance(context).bookDao())
+ }
+ override val authorRepository: AuthorRepository by lazy {
+ OfflineAuthorRepository(AppDatabase.getInstance(context).authorDao())
+ }
+ override val userRepository: UserRepository by lazy {
+ OfflineUserRepository(AppDatabase.getInstance(context).userDao())
+ }
+
+ companion object {
+ const val TIMEOUT = 5000L
+ }
+}
\ 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
index 1638411..05c0b67 100644
--- a/app/src/main/java/com/example/myapplication/db/database/AppDatabase.kt
+++ b/app/src/main/java/com/example/myapplication/db/database/AppDatabase.kt
@@ -1,9 +1,12 @@
package com.example.myapplication.db.database
import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -17,18 +20,19 @@ import com.example.myapplication.db.model.Author;
import com.example.myapplication.R
@Database(entities = [Book::class, Author::class, User::class], version = 1, exportSchema = false)
+@TypeConverters(Converters::class)
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"
+ private const val DB_NAME: String = "PMU1"
@Volatile
private var INSTANCE: AppDatabase? = null
- private suspend fun populateDatabase() {
+ private suspend fun populateDatabase(context: Context) {
INSTANCE?.let { database ->
//Users
val userDao = database.userDao()
@@ -46,10 +50,10 @@ abstract class AppDatabase : RoomDatabase() {
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)
+ val book1 = Book(1,"1984", "Роман о том, как репрессивная машина тоталитарного государства может уничтожить любую личность.", "Был холодный ясный апрельский день, и часы пробили тринадцать.\n" + "Уинстон Смит, прижав подбородок к груди и ежась от омерзительного ветра, быстро скользнул в стеклянные двери Дома Победы, но все же вихрь песка и пыли успел ворваться вместе с ним.", getBookImage(context,"or1984"), 1, 1)
+ val book2 = Book(2,"Собачье сердце", "Роман о бродячем псе Шарике, превращенном в человека.", "У‑у-у-у-у-гу-гуг-гуу! О, гляньте на меня, я погибаю.\n" + "Вьюга в подворотне ревёт мне отходную, и я вою с ней.\n" + "Пропал я, пропал.", getBookImage(context,"dogsheart"), 2, 1)
+ val book3 = Book(3,"Вельд", "Рассказ о зависимости от технических устройств, потере человечности.", "— Джорджи, пожалуйста, посмотри детскую комнату.\n" + "— А что с ней?\n" + "— Не знаю.\n" + "— Так в чем же дело?", getBookImage(context,"veld"), 3, 1)
+ val book4 = Book(4,"Роковые яйца", "Рассказ, критикующий стремление к прогрессу без учета последствий.","16 апреля 1928 года, вечером, профессор зоологии IV государственного университета и директор зооинститута в Москве Персиков вошел в свой кабинет, помещающийся в зооинституте, что на улице Герцена.\n" + "Профессор зажег верхний матовый шар и огляделся.", getBookImage(context,"eggs"), 2, 2)
bookDao.insert(book1)
bookDao.insert(book2)
bookDao.insert(book3)
@@ -68,7 +72,7 @@ abstract class AppDatabase : RoomDatabase() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
CoroutineScope(Dispatchers.IO).launch {
- populateDatabase()
+ populateDatabase(appContext)
}
}
})
@@ -76,5 +80,10 @@ abstract class AppDatabase : RoomDatabase() {
.also { INSTANCE = it }
}
}
+ private fun getBookImage(context: Context, imageId: String): Bitmap {
+ val inputStream = context.assets.open("${imageId}.jpg")
+ val bitmap = BitmapFactory.decodeStream(inputStream)
+ return bitmap
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/db/database/Converters.kt b/app/src/main/java/com/example/myapplication/db/database/Converters.kt
new file mode 100644
index 0000000..06ee9f3
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/db/database/Converters.kt
@@ -0,0 +1,20 @@
+package com.example.myapplication.db.database
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import androidx.room.TypeConverter
+import java.io.ByteArrayOutputStream
+
+class Converters {
+ @TypeConverter
+ fun fromBitmap(bitmap: Bitmap): ByteArray {
+ val outputStream = ByteArrayOutputStream()
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 1, outputStream)
+ return outputStream.toByteArray()
+ }
+
+ @TypeConverter
+ fun toBitmap(byteArray: ByteArray): Bitmap {
+ return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
+ }
+}
\ 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
index 39834d2..d49932f 100644
--- a/app/src/main/java/com/example/myapplication/db/model/Author.kt
+++ b/app/src/main/java/com/example/myapplication/db/model/Author.kt
@@ -8,14 +8,10 @@ import androidx.room.PrimaryKey
@Entity(tableName = "authors")
data class Author(
@PrimaryKey(autoGenerate = true)
- val uid: Int?,
+ val uid: Int = 0,
@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
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
index 21a871e..5b3b422 100644
--- a/app/src/main/java/com/example/myapplication/db/model/Book.kt
+++ b/app/src/main/java/com/example/myapplication/db/model/Book.kt
@@ -1,5 +1,6 @@
package com.example.myapplication.db.model
+import android.graphics.Bitmap
import androidx.annotation.DrawableRes
import androidx.room.ColumnInfo
import androidx.room.Entity
@@ -20,14 +21,14 @@ import androidx.room.Ignore
entity = User::class,
parentColumns = ["uid"],
childColumns = ["user_id"],
- onDelete = ForeignKey.RESTRICT,
- onUpdate = ForeignKey.RESTRICT
+ onDelete = ForeignKey.CASCADE,
+ onUpdate = ForeignKey.CASCADE
)
]
)
data class Book(
@PrimaryKey(autoGenerate = true)
- val uid: Int?,
+ val uid: Int = 0,
@ColumnInfo(name = "title")
val title: String,
@ColumnInfo(name = "description")
@@ -35,21 +36,12 @@ data class Book(
@ColumnInfo(name = "content")
val content: String,
@ColumnInfo(name = "cover")
- @DrawableRes val cover: Int?,
+ val cover: Bitmap?,
@ColumnInfo(name = "author_id", index = true)
- val authorId: Int?,
+ val authorId: Int,
@ColumnInfo(name = "user_id", index = true)
- val userId: Int?
+ 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
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
index 80eeb89..2003848 100644
--- a/app/src/main/java/com/example/myapplication/db/model/User.kt
+++ b/app/src/main/java/com/example/myapplication/db/model/User.kt
@@ -9,7 +9,7 @@ import androidx.room.Index
@Entity(tableName = "users", indices = [(Index(value = ["login"], unique = true))])
data class User(
@PrimaryKey(autoGenerate = true)
- val uid: Int?,
+ val uid: Int = 0,
@ColumnInfo(name = "login")
var login: String,
@ColumnInfo(name = "password")
@@ -19,13 +19,6 @@ data class User(
@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
diff --git a/app/src/main/java/com/example/myapplication/db/respository/AuthorRepository.kt b/app/src/main/java/com/example/myapplication/db/respository/AuthorRepository.kt
new file mode 100644
index 0000000..43d38b9
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/db/respository/AuthorRepository.kt
@@ -0,0 +1,15 @@
+package com.example.myapplication.db.respository
+
+import androidx.paging.PagingData
+import com.example.myapplication.db.model.Author
+import kotlinx.coroutines.flow.Flow
+
+interface AuthorRepository {
+ suspend fun getAll(): List
+ suspend fun getAllDrop(): List
+ suspend fun getByUid(uid: Int): Author
+ fun loadAllAuthorsPaged(): Flow>
+ suspend fun insert(author: Author)
+ suspend fun update(author: Author)
+ suspend fun delete(author: Author)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/db/respository/BookRepository.kt b/app/src/main/java/com/example/myapplication/db/respository/BookRepository.kt
new file mode 100644
index 0000000..9b8801a
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/db/respository/BookRepository.kt
@@ -0,0 +1,19 @@
+package com.example.myapplication.db.respository
+
+import androidx.paging.PagingData
+import androidx.paging.PagingSource
+import com.example.myapplication.db.model.Book
+import com.example.myapplication.db.model.BookWithAuthor
+import kotlinx.coroutines.flow.Flow
+
+interface BookRepository {
+ suspend fun getAll(): List
+ suspend fun getByUid(uid: Int): Book
+ suspend fun getBySearch(searchStr: String): List
+ fun getByUserId(userId: Int): Flow>
+ fun getByAuthorId(authorId: Int): Flow>
+ fun loadAllBooksPaged(): Flow>
+ suspend fun insert(book: Book)
+ suspend fun update(book: Book)
+ suspend fun delete(book: Book)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/db/respository/OfflineAuthorRepository.kt b/app/src/main/java/com/example/myapplication/db/respository/OfflineAuthorRepository.kt
new file mode 100644
index 0000000..d7dd8ae
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/db/respository/OfflineAuthorRepository.kt
@@ -0,0 +1,29 @@
+package com.example.myapplication.db.respository
+
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import androidx.paging.PagingSource
+import com.example.myapplication.db.database.AppContainer
+import com.example.myapplication.db.dao.AuthorDao
+import com.example.myapplication.db.model.Author
+import kotlinx.coroutines.flow.Flow
+
+class OfflineAuthorRepository(private val authorDao: AuthorDao) : AuthorRepository {
+ override suspend fun getAll(): List = authorDao.getAll()
+ override suspend fun getAllDrop(): List = authorDao.getAllDrop()
+ override suspend fun getByUid(uid: Int): Author = authorDao.getByUid(uid)
+ override fun loadAllAuthorsPaged(): Flow> = Pager(
+ config = PagingConfig(
+ pageSize = AppContainer.LIMIT,
+ enablePlaceholders = false
+ ),
+ pagingSourceFactory = authorDao::loadAllAuthorsPaged
+ ).flow
+ fun loadAuthorsPaged(): PagingSource = authorDao.loadAllAuthorsPaged()
+ override suspend fun insert(author: Author) = authorDao.insert(author)
+
+ override suspend fun update(author: Author) = authorDao.update(author)
+
+ override suspend fun delete(author: Author) = authorDao.delete(author)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/db/respository/OfflineBookRepository.kt b/app/src/main/java/com/example/myapplication/db/respository/OfflineBookRepository.kt
new file mode 100644
index 0000000..f372449
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/db/respository/OfflineBookRepository.kt
@@ -0,0 +1,35 @@
+package com.example.myapplication.db.respository
+
+import androidx.paging.Pager
+import androidx.paging.PagingConfig
+import androidx.paging.PagingData
+import androidx.paging.PagingSource
+import com.example.myapplication.db.database.AppContainer
+import com.example.myapplication.db.dao.BookDao
+import com.example.myapplication.db.model.Book
+import com.example.myapplication.db.model.BookWithAuthor
+import kotlinx.coroutines.flow.Flow
+
+class OfflineBookRepository(private val bookDao: BookDao) : BookRepository {
+ override suspend fun getAll(): List = bookDao.getAll()
+
+ override suspend fun getByUid(uid: Int): Book = bookDao.getByUid(uid)
+
+ override suspend fun getBySearch(searchStr: String): List = bookDao.getBySearch(searchStr)
+
+ override fun getByUserId(userId: Int): Flow> = bookDao.getByUserId(userId)
+ override fun getByAuthorId(authorId: Int): Flow> = bookDao.getByAuthorId(authorId)
+ override fun loadAllBooksPaged(): Flow> = Pager(
+ config = PagingConfig(
+ pageSize = AppContainer.LIMIT,
+ enablePlaceholders = false
+ ),
+ pagingSourceFactory = bookDao::loadAllBooksPaged
+ ).flow
+ fun loadBooksPaged(): PagingSource = bookDao.loadAllBooksPaged()
+ override suspend fun insert(book: Book) = bookDao.insert(book)
+
+ override suspend fun update(book: Book) = bookDao.update(book)
+
+ override suspend fun delete(book: Book) = bookDao.delete(book)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/db/respository/OfflineUserRepository.kt b/app/src/main/java/com/example/myapplication/db/respository/OfflineUserRepository.kt
new file mode 100644
index 0000000..2eecd37
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/db/respository/OfflineUserRepository.kt
@@ -0,0 +1,22 @@
+package com.example.myapplication.db.respository
+
+import com.example.myapplication.db.dao.UserDao;
+import com.example.myapplication.db.model.User;
+import com.example.myapplication.db.model.Book;
+import kotlinx.coroutines.flow.Flow
+
+class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
+ override fun getAll(): Flow> = userDao.getAll()
+
+ override suspend fun getByUid(uid: Int): User = userDao.getByUid(uid)
+
+ override fun getUserBooks(userid: Int): Flow> = userDao.getUserBooks(userid)
+
+ override suspend fun tryLogin(login: String, password: String): User? = userDao.tryLogin(login, password)
+
+ override suspend fun insert(user: User) = userDao.insert(user)
+
+ override suspend fun update(user: User) = userDao.update(user)
+
+ override suspend fun delete(user: User) = userDao.delete(user)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/db/respository/UserRepository.kt b/app/src/main/java/com/example/myapplication/db/respository/UserRepository.kt
new file mode 100644
index 0000000..d14dfce
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/db/respository/UserRepository.kt
@@ -0,0 +1,14 @@
+package com.example.myapplication.db.respository
+
+import com.example.myapplication.db.model.User;
+import com.example.myapplication.db.model.Book;
+import kotlinx.coroutines.flow.Flow
+interface UserRepository {
+ fun getAll(): Flow>
+ suspend fun getByUid(uid: Int): User
+ fun getUserBooks(userid: Int): Flow>
+ suspend fun tryLogin(login: String, password: String): User?
+ suspend fun insert(user: User)
+ suspend fun update(user: User)
+ suspend fun delete(user: User)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/ui/theme/Theme.kt b/app/src/main/java/com/example/myapplication/ui/theme/Theme.kt
index ffb65c5..1f49e92 100644
--- a/app/src/main/java/com/example/myapplication/ui/theme/Theme.kt
+++ b/app/src/main/java/com/example/myapplication/ui/theme/Theme.kt
@@ -17,9 +17,9 @@ import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
- primary = Purple80,
- secondary = PurpleGrey80,
- tertiary = Pink80
+ primary = Green,
+ secondary = Grey,
+ tertiary = Pink40,
)
private val LightColorScheme = lightColorScheme(
diff --git a/app/src/main/res/drawable/ic_camera.jpg b/app/src/main/res/drawable/ic_camera.jpg
new file mode 100644
index 0000000..07567af
Binary files /dev/null and b/app/src/main/res/drawable/ic_camera.jpg differ
diff --git a/app/src/main/res/drawable/img.jpg b/app/src/main/res/drawable/img.jpg
new file mode 100644
index 0000000..4a2c71b
Binary files /dev/null and b/app/src/main/res/drawable/img.jpg differ
diff --git a/app/src/main/res/drawable/user.png b/app/src/main/res/drawable/user.png
new file mode 100644
index 0000000..6d15e9e
Binary files /dev/null and b/app/src/main/res/drawable/user.png differ