This commit is contained in:
ityurner02@mail.ru 2023-12-13 18:53:28 +04:00
parent 62619bd13d
commit d5980d6ff2
59 changed files with 1834 additions and 554 deletions

View File

@ -37,5 +37,6 @@
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="UnusedEquals" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

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

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
app/src/main/assets/img.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -1,5 +1,6 @@
package com.example.myapplication
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
import androidx.activity.ComponentActivity
@ -16,6 +17,7 @@ import com.example.myapplication.ui.theme.MyApplicationTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MainActivity.appContext = applicationContext
setContent {
MyApplicationTheme {
Surface(
@ -27,6 +29,11 @@ class MainActivity : ComponentActivity() {
}
}
}
companion object {
lateinit var appContext: Context
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)

View File

@ -0,0 +1,14 @@
package com.example.myapplication
import android.app.Application
import com.example.myapplication.db.database.AppContainer
import com.example.myapplication.db.database.AppDataContainer
class MyApplication : Application() {
lateinit var container: AppContainer
override fun onCreate() {
super.onCreate()
container = AppDataContainer(this)
}
}

View File

@ -0,0 +1,14 @@
package com.example.myapplication
class SingletonClass{
var _userId = 2
var _role = "USER"
fun getUserId(): Int{ return _userId }
fun setUserId(userId: Int){
_userId = userId
}
fun getRole(): String{ return _role }
fun setRole(role: String){
_role = role
}
}

View File

@ -0,0 +1,76 @@
package com.example.myapplication.composeui
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.myapplication.MyApplication
import com.example.myapplication.composeui.ViewModel.AuthorDropDownViewModel
import com.example.myapplication.composeui.ViewModel.AuthorEditViewModel
import com.example.myapplication.composeui.ViewModel.AuthorListViewModel
import com.example.myapplication.composeui.ViewModel.BookEditViewModel
import com.example.myapplication.composeui.ViewModel.BookListViewModel
import com.example.myapplication.composeui.ViewModel.BookPageViewModel
import com.example.myapplication.composeui.ViewModel.SearchPageViewModel
import com.example.myapplication.composeui.ViewModel.UserEditViewModel
import com.example.myapplication.composeui.ViewModel.UserPageViewModel
object AppViewModelProvider {
val Factory = viewModelFactory {
initializer {
UserPageViewModel(
this.createSavedStateHandle(),
myApplication().container.userRepository
)
}
initializer {
UserEditViewModel(
this.createSavedStateHandle(),
myApplication().container.userRepository
)
}
initializer {
SearchPageViewModel(
myApplication().container.bookRepository,
this.createSavedStateHandle()
)
}
initializer {
BookPageViewModel(
myApplication().container.bookRepository,
this.createSavedStateHandle()
)
}
initializer {
BookListViewModel(
myApplication().container.bookRepository
)
}
initializer {
BookEditViewModel(
this.createSavedStateHandle(),
myApplication().container.bookRepository
)
}
initializer {
AuthorListViewModel(
myApplication().container.authorRepository
)
}
initializer {
AuthorEditViewModel(
this.createSavedStateHandle(),
myApplication().container.authorRepository
)
}
initializer {
AuthorDropDownViewModel(
myApplication().container.authorRepository
)
}
}
}
fun CreationExtras.myApplication(): MyApplication =
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as MyApplication)

View File

@ -0,0 +1,97 @@
package com.example.myapplication.composeui
import android.util.Log
import android.widget.Toast
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.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.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.MainActivity
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 kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import java.lang.Exception
@Composable
fun AuthorCell(navController: NavController?, author: Author, viewModel: AuthorListViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val scope = rememberCoroutineScope()
val context = LocalContext.current
Column(modifier = Modifier
.padding(all = 5.dp)
.requiredSize(170.dp, 100.dp)
.clip(RoundedCornerShape(10.dp))
.border(1.dp, Color.LightGray, shape = RoundedCornerShape(10.dp))
) {
Text("${author.name}")
Row{
Button(
modifier = Modifier
.padding(all = 5.dp),
onClick = {
val route = Screen.AuthorEdit.route.replace("{id}", author.uid.toString())
navController!!.navigate(route)
},
) {
Icon(
imageVector = Icons.Default.Create,
contentDescription = "Изменить",
tint = Color.White
)
}
Button(
modifier = Modifier
.padding(all = 5.dp),
onClick = {
scope.launch {
if (AppDatabase.getInstance(context).bookDao().getByAuthorId(author.uid).firstOrNull().toString() == "[]") {
viewModel.deleteAuthor(author)
} else {
val toast = Toast.makeText(
MainActivity.appContext,
"Невозможно удалить, есть книги данного автора",
Toast.LENGTH_SHORT
)
toast.show()
}
}
},
colors = ButtonDefaults.buttonColors(containerColor = Color.Red),
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Удалить",
tint = Color.White
)
}
}
}
}

View File

@ -0,0 +1,90 @@
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.AuthorsListUiState
import com.example.myapplication.composeui.ViewModel.AuthorsUiState
import com.example.myapplication.composeui.ViewModel.AuthorDetails
import com.example.myapplication.composeui.ViewModel.AuthorEditViewModel
import com.example.myapplication.composeui.ViewModel.AuthorUiState
import com.example.myapplication.composeui.ViewModel.ImageUploader
import com.example.myapplication.db.model.Author
import kotlinx.coroutines.launch
@Composable
fun AuthorEdit(navController: NavController, viewModel: AuthorEditViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val coroutineScope = rememberCoroutineScope()
AuthorEdit(
authorUiState = viewModel.authorUiState,
onClick = {
coroutineScope.launch {
viewModel.saveAuthor()
navController.popBackStack()
}
},
onUpdate = viewModel::updateUiState,
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun AuthorEdit(
authorUiState: AuthorUiState,
onClick: () -> 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 = "Сохранить")
}
}
}

View File

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

View File

@ -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 = "Сохранить")
}
}
}

View File

@ -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<BookWithAuthor?>(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)
}
}

View File

@ -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<Book>() }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).bookDao().getBySearch(text).collect { data ->
books.clear()
books.addAll(data)
}
}
}
Column(Modifier.padding(all = 10.dp)) {
LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 180.dp)) {
items (books){ book ->
key(book.uid) {
val bookId = Screen.BookView.route.replace("{id}", book.uid.toString())
Image(
painter = painterResource(id = book.cover!!),
contentDescription = book.title,
modifier = Modifier
.height(300.dp)
.padding(horizontal = 5.dp)
.clickable(
enabled = true,
onClick = { navController?.navigate(bookId) }
)
)
}
}
}
Spacer(Modifier.padding(bottom = 10.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
onClick = { navController?.navigate(Screen.Loader.route) }) {
Text(stringResource(id = R.string.add_book))
fun BookSearch(navController: NavController, searchStr: String, viewModel: SearchPageViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
val coroutineScope = rememberCoroutineScope()
val searchPageUiState = viewModel.searchPageUiState
val searchBook: List<Book> = 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(), "")
}
}
}

View File

@ -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<BookWithAuthor?>(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)
}
}
}

View File

@ -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<Book>() }
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<Book> = 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())
}
}
}

View File

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

View File

@ -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<Author>() }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).authorDao().getAll().collect { data ->
authors.clear()
authors.addAll(data)
}
}
}
Column(Modifier.padding(all = 10.dp)) {
authors.forEach{ author ->
Text(author.name, fontSize = 20.sp)
}
Spacer(Modifier.padding(bottom = 10.dp))
fun ListAuthors(navController: NavController, viewModel: AuthorListViewModel = viewModel(factory = AppViewModelProvider.Factory)){
val coroutineScope = rememberCoroutineScope()
val authorListUiState = viewModel.authorListUiState
val pagingAuthor: LazyPagingItems<Author> = 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())
}
}
}

View File

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

View File

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

View File

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

View File

@ -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("Назад")
}
}
}
}

View File

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

View File

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

View File

@ -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<Author> = listOf())
data class AuthorsUiState(
val author: Author? = null
)
fun Author.toUiState() = AuthorsUiState(author = Author(uid = uid, name = name))

View File

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

View File

@ -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<PagingData<Author>> = authorRepository.loadAllAuthorsPaged()
suspend fun deleteAuthor(author: Author) {
authorRepository.delete(author)
}
}
data class AuthorListUiState(val authorList: List<Author> = listOf())

View File

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

View File

@ -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<PagingData<Book>> = bookRepository.loadAllBooksPaged()
suspend fun deleteBook(book: Book) {
bookRepository.delete(book)
}
}
data class BookListUiState(val bookList: List<Book> = listOf())

View File

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

View File

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

View File

@ -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<PagingData<Book>> = bookRepository.loadAllBooksPaged()
}
data class SearchPageUiState(val bookList: List<Book> = listOf())

View File

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

View File

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

View File

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

View File

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

View File

@ -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<List<Author>>
suspend fun getAll(): List<Author>
@Query("select * from authors order by author_name collate nocase asc")
suspend fun getAllDrop(): List<Author>
@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<Int, Author>
@Insert
suspend fun insert(author: Author)

View File

@ -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<List<Book>>
suspend fun getAll(): List<Book>
@Query("select * from books left join authors on books.author_id = authors.uid where books.uid = :uid")
suspend fun getByUid(uid: Int): BookWithAuthor
@Query("select * from books where books.uid = :uid")
suspend fun getByUid(uid: Int): Book
@Query("select * from books where books.title LIKE '%' || :searchStr || '%'")
fun getBySearch(searchStr: String): Flow<List<Book>>
suspend fun getBySearch(searchStr: String): List<Book>
@Query("select * from books where books.user_id = :userId")
fun getByUserId(userId: Int): Flow<List<Book>>
@ -26,6 +27,9 @@ interface BookDao {
@Query("select * from books where books.author_id = :authorId")
fun getByAuthorId(authorId: Int): Flow<List<Book>>
@Query("select * from books order by title collate nocase asc")
fun loadAllBooksPaged(): PagingSource<Int, Book>
@Insert
suspend fun insert(book: Book)

View File

@ -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<List<User>>
@Query("select * from users where login = :login and password = :password")
suspend fun tryLogin(login: String, password: String): User?
@Query("select * from users where users.uid = :uid")
suspend fun getByUid(uid: Int): User
@Query("select * from books where books.user_id = :userid")
fun getUserBooks(userid: Int): Flow<List<Book>>
@Query("select * from users where login = :login and password = :password")
suspend fun tryLogin(login: String, password: String): User?
@Insert
suspend fun insert(user: User)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Author>
suspend fun getAllDrop(): List<Author>
suspend fun getByUid(uid: Int): Author
fun loadAllAuthorsPaged(): Flow<PagingData<Author>>
suspend fun insert(author: Author)
suspend fun update(author: Author)
suspend fun delete(author: Author)
}

View File

@ -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<Book>
suspend fun getByUid(uid: Int): Book
suspend fun getBySearch(searchStr: String): List<Book>
fun getByUserId(userId: Int): Flow<List<Book>>
fun getByAuthorId(authorId: Int): Flow<List<Book>>
fun loadAllBooksPaged(): Flow<PagingData<Book>>
suspend fun insert(book: Book)
suspend fun update(book: Book)
suspend fun delete(book: Book)
}

View File

@ -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<Author> = authorDao.getAll()
override suspend fun getAllDrop(): List<Author> = authorDao.getAllDrop()
override suspend fun getByUid(uid: Int): Author = authorDao.getByUid(uid)
override fun loadAllAuthorsPaged(): Flow<PagingData<Author>> = Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = authorDao::loadAllAuthorsPaged
).flow
fun loadAuthorsPaged(): PagingSource<Int, Author> = 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)
}

View File

@ -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<Book> = bookDao.getAll()
override suspend fun getByUid(uid: Int): Book = bookDao.getByUid(uid)
override suspend fun getBySearch(searchStr: String): List<Book> = bookDao.getBySearch(searchStr)
override fun getByUserId(userId: Int): Flow<List<Book>> = bookDao.getByUserId(userId)
override fun getByAuthorId(authorId: Int): Flow<List<Book>> = bookDao.getByAuthorId(authorId)
override fun loadAllBooksPaged(): Flow<PagingData<Book>> = Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = bookDao::loadAllBooksPaged
).flow
fun loadBooksPaged(): PagingSource<Int, Book> = 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)
}

View File

@ -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<List<User>> = userDao.getAll()
override suspend fun getByUid(uid: Int): User = userDao.getByUid(uid)
override fun getUserBooks(userid: Int): Flow<List<Book>> = 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)
}

View File

@ -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<List<User>>
suspend fun getByUid(uid: Int): User
fun getUserBooks(userid: Int): Flow<List<Book>>
suspend fun tryLogin(login: String, password: String): User?
suspend fun insert(user: User)
suspend fun update(user: User)
suspend fun delete(user: User)
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB