I am pdfile

This commit is contained in:
maxnes3 2023-12-28 03:14:59 +04:00
parent 7baad4c44e
commit a29f8803ae
9 changed files with 341 additions and 165 deletions

View File

@ -0,0 +1,5 @@
package com.example.mobileapp.api
enum class ApiStatus {
LOADING, ERROR, DONE, NONE
}

View File

@ -13,9 +13,11 @@ class RemoteConverters {
fun fromBitmap(bitmap: Bitmap): String {
val outputStream = ByteArrayOutputStream()
val extendedBitmap = scaleRatioBitmap(bitmap)
// Сжимаем изображение до указанного максимального размера
val quality = calculateQuality(bitmap)
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
val quality = calculateQuality(extendedBitmap)
extendedBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream)
val byteArray = outputStream.toByteArray()
return Base64.encodeToString(byteArray, Base64.NO_WRAP)
@ -28,8 +30,6 @@ class RemoteConverters {
private fun calculateQuality(bitmap: Bitmap): Int {
val outputStream = ByteArrayOutputStream()
/*bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
val initialSize = outputStream.size()*/
// Уменьшаем качество изображения, пока его размер не станет меньше максимального
var quality = 100
@ -42,5 +42,21 @@ class RemoteConverters {
// Возвращаем качество, при котором размер изображения удовлетворяет ограничению
return if (quality < 0) 0 else quality
}
private fun scaleRatioBitmap(bitmap: Bitmap): Bitmap {
val maxWidth = 990
val maxHeight = 990
if (bitmap.width > maxWidth || bitmap.height > maxHeight) {
// Если размер превышает максимальный, масштабируем изображение
val ratio = Math.min(maxWidth.toFloat() / bitmap.width,
maxHeight.toFloat() / bitmap.height)
val width = (ratio * bitmap.width).toInt()
val height = (ratio * bitmap.height).toInt()
return Bitmap.createScaledBitmap(bitmap, width, height, true)
}
return bitmap
}
}
}

View File

@ -352,16 +352,3 @@ fun DialogWindow(label: String, message: String, onConfirmAction: () -> Unit, on
}
)
}
@Composable
fun LoadingScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
CircularProgressIndicator()
}
}

View File

@ -0,0 +1,75 @@
package com.example.mobileapp.database.viewmodels
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.mobileapp.api.ApiStatus
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.io.IOException
open class CustomViewModel : ViewModel() {
var apiStatus by mutableStateOf(ApiStatus.NONE)
private set
var apiError by mutableStateOf("")
private set
fun runInScope(
actionSuccess: suspend () -> Unit,
actionError: suspend () -> Unit
) {
viewModelScope.launch {
apiStatus = ApiStatus.LOADING
runCatching {
actionSuccess()
apiStatus = ApiStatus.DONE
apiError = ""
}.onFailure { e: Throwable ->
when (e) {
is IOException,
is HttpException -> {
actionError()
apiStatus = ApiStatus.ERROR
apiError = e.localizedMessage ?: e.toString()
}
else -> throw e
}
}
}
}
fun runInScope(actionSuccess: suspend () -> Unit) {
runInScope(actionSuccess, actionError = {})
}
fun clearStatus(){
apiStatus = ApiStatus.NONE
}
}
@Composable
fun LoadingScreen(color: Color) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
color = color,
strokeWidth = 6.dp
)
}
}

View File

@ -11,7 +11,7 @@ import com.example.mobileapp.database.repositories.UserRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
class UserViewModel(private val userRepository: UserRepository): ViewModel() {
class UserViewModel(private val userRepository: UserRepository): CustomViewModel() {
//val getAllUsers = userRepository.getAllUsers()
suspend fun getUser(id: Int): User? = userRepository.getUser(id)
@ -28,8 +28,12 @@ class UserViewModel(private val userRepository: UserRepository): ViewModel() {
if (user.password.isEmpty()){
return@launch
}
userRepository.updateUser(user)
GlobalUser.getInstance().setUser(user)
runInScope(
actionSuccess = {
userRepository.updateUser(user)
GlobalUser.getInstance().setUser(user)
}
)
}
fun deleteUser(user: User) = viewModelScope.launch {
@ -44,18 +48,38 @@ class UserViewModel(private val userRepository: UserRepository): ViewModel() {
if(user.email.isEmpty() || !isValidEmail(user.email)){
return@launch
}
userRepository.insertUser(user)
runInScope(
actionSuccess = {
userRepository.insertUser(user)
GlobalUser.getInstance().setUser(userRepository.getUserByLogin(
UserRemoteSignIn(user.login, user.password)))
}
)
/*userRepository.insertUser(user)
GlobalUser.getInstance().setUser(userRepository.getUserByLogin(
UserRemoteSignIn(user.login, user.password)))
UserRemoteSignIn(user.login, user.password)))*/
}
fun authUser(user: User) = viewModelScope.launch {
val globalUser = userRepository.getUserByLogin(UserRemoteSignIn(user.login, user.password))
runInScope(
actionSuccess = {
val globalUser = userRepository.getUserByLogin(UserRemoteSignIn(user.login, user.password))
globalUser?.let {
if (user.password.isNotEmpty() && user.password == globalUser.password){
GlobalUser.getInstance().setUser(globalUser)
}
}
},
actionError = {
GlobalUser.getInstance().setUser(null)
}
)
/*val globalUser = userRepository.getUserByLogin(UserRemoteSignIn(user.login, user.password))
globalUser?.let {
if (user.password.isNotEmpty() && user.password == globalUser.password){
GlobalUser.getInstance().setUser(globalUser)
}
}
}*/
}
private fun isValidEmail(email: String): Boolean {

View File

@ -1,5 +1,6 @@
package com.example.mobileapp.screens
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@ -23,12 +24,14 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.mobileapp.GlobalUser
import com.example.mobileapp.R
import com.example.mobileapp.api.ApiStatus
import com.example.mobileapp.components.ActiveButton
import com.example.mobileapp.components.NavigationButton
import com.example.mobileapp.components.PasswordInputField
import com.example.mobileapp.components.PlaceholderInputField
import com.example.mobileapp.database.MobileAppDataBase
import com.example.mobileapp.database.entities.User
import com.example.mobileapp.database.viewmodels.LoadingScreen
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.UserViewModel
import com.example.mobileapp.ui.theme.ButtonColor1
@ -41,49 +44,63 @@ fun Authorization(navController: NavHostController,
userViewModel: UserViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val isAuthorizated = remember { mutableStateOf(false) }
val context = LocalContext.current
if(GlobalUser.getInstance().getUser() != null && !isAuthorizated.value) {
isAuthorizated.value = !isAuthorizated.value
navController.navigate("main")
when(userViewModel.apiStatus){
ApiStatus.DONE -> {
navController.navigate("main")
userViewModel.clearStatus()
}
ApiStatus.LOADING -> LoadingScreen(ButtonColor2)
ApiStatus.ERROR -> Toast.makeText(context, "Не верные данные или пользователя не существует: "
+ userViewModel.apiError, Toast.LENGTH_SHORT).show()
else -> {}
}
val login = remember { mutableStateOf("") }
val password = remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp),
verticalArrangement = Arrangement.Bottom
) {
Image(
painter = painterResource(id = R.drawable.login),
contentDescription = "login",
contentScale = ContentScale.Crop,
if(userViewModel.apiStatus != ApiStatus.LOADING) {
Column(
modifier = Modifier
.size(448.dp)
.padding(8.dp)
.align(Alignment.CenterHorizontally))
PlaceholderInputField(label = "Логин", isSingleLine = true, onTextChanged = {newlogin ->
login.value = newlogin
})
PasswordInputField(label = "Пароль", onPasswordChanged = {newpassword ->
password.value = newpassword
})
ActiveButton(label = "Вход", backgroundColor = ButtonColor2,
textColor = Color.White, onClickAction = {
if (login.value.isNotEmpty() && password.value.isNotEmpty()) {
userViewModel.authUser(
User(
login = login.value,
password = password.value,
email = String()
.fillMaxSize()
.padding(bottom = 8.dp),
verticalArrangement = Arrangement.Bottom
) {
Image(
painter = painterResource(id = R.drawable.login),
contentDescription = "login",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(448.dp)
.padding(8.dp)
.align(Alignment.CenterHorizontally)
)
PlaceholderInputField(
label = "Логин",
isSingleLine = true,
onTextChanged = { newlogin ->
login.value = newlogin
})
PasswordInputField(label = "Пароль", onPasswordChanged = { newpassword ->
password.value = newpassword
})
ActiveButton(label = "Вход", backgroundColor = ButtonColor2,
textColor = Color.White, onClickAction = {
if (login.value.isNotEmpty() && password.value.isNotEmpty()) {
userViewModel.authUser(
User(
login = login.value,
password = password.value,
email = String()
)
)
)
}
})
NavigationButton(navController = navController, destination = "registration", label = "Регистрация",
backgroundColor = ButtonColor1, textColor = Color.Black)
}
})
NavigationButton(
navController = navController, destination = "registration", label = "Регистрация",
backgroundColor = ButtonColor1, textColor = Color.Black
)
}
}
}

View File

@ -6,6 +6,7 @@ import android.graphics.ImageDecoder
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.BorderStroke
@ -44,12 +45,14 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.mobileapp.GlobalUser
import com.example.mobileapp.R
import com.example.mobileapp.api.ApiStatus
import com.example.mobileapp.components.ActiveButton
import com.example.mobileapp.components.NavigationButton
import com.example.mobileapp.components.PlaceholderInputField
import com.example.mobileapp.database.entities.Mail
import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.entities.User
import com.example.mobileapp.database.viewmodels.LoadingScreen
import com.example.mobileapp.database.viewmodels.MailViewModel
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.StoryViewModel
@ -207,6 +210,16 @@ fun EditUserScreen(navController: NavHostController,
factory = MobileAppViewModelProvider.Factory
)) {
val context = LocalContext.current
when(userViewModel.apiStatus){
ApiStatus.DONE -> {
navController.navigate("settings")
userViewModel.clearStatus()
}
ApiStatus.LOADING -> LoadingScreen(ButtonColor2)
ApiStatus.ERROR -> Toast.makeText(context, "Не удалось обновить данные пользователя: "
+ userViewModel.apiError, Toast.LENGTH_SHORT).show()
else -> {}
}
var userId = remember { mutableStateOf(0) }
val photo = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.photoplaceholder)) }
@ -241,65 +254,76 @@ fun EditUserScreen(navController: NavHostController,
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.Bottom
) {
Box(
if(userViewModel.apiStatus != ApiStatus.LOADING) {
Column(
modifier = Modifier
.size(512.dp),
contentAlignment = Alignment.Center
.fillMaxSize()
.padding(bottom = 8.dp)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.Bottom
) {
Image(
bitmap = photo.value.asImageBitmap(),
contentDescription = "Background Image",
contentScale = ContentScale.Crop,
Box(
modifier = Modifier
.size(512.dp)
.blur(12.dp),
colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) }))
Image(
bitmap = photo.value.asImageBitmap(),
contentDescription = "editplaceholder",
contentScale = ContentScale.Crop,
modifier = Modifier
.padding(8.dp)
.clip(CircleShape)
.size(384.dp))
}
ActiveButton(label = "Выбрать фото", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = {
launcher.launch("image/*")
})
PlaceholderInputField(label = "Никнейм", isSingleLine = true,
startValue = login.value, onTextChanged = { newLogin ->
login.value = newLogin
})
PlaceholderInputField(label = "Пароль", isSingleLine = true,
startValue = password.value, onTextChanged = { newPassword ->
password.value = newPassword
})
PlaceholderInputField(label = "Почта", isSingleLine = true,
startValue = email.value, onTextChanged = { newEmail ->
email.value = newEmail
})
ActiveButton(label = "Сохранить", backgroundColor = ButtonColor1, textColor = Color.Black, onClickAction = {
userViewModel.updateUser(
User(
id = userId.value,
login = login.value,
password = password.value,
email = email.value,
photo = photo.value
.size(512.dp),
contentAlignment = Alignment.Center
) {
Image(
bitmap = photo.value.asImageBitmap(),
contentDescription = "Background Image",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(512.dp)
.blur(12.dp),
colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) })
)
)
navController.navigate("settings")
})
ActiveButton(label = "Назад", backgroundColor = ButtonColor2, textColor = Color.White,
onClickAction = {
navController.navigate("settings")
})
Image(
bitmap = photo.value.asImageBitmap(),
contentDescription = "editplaceholder",
contentScale = ContentScale.Crop,
modifier = Modifier
.padding(8.dp)
.clip(CircleShape)
.size(384.dp)
)
}
ActiveButton(
label = "Выбрать фото",
backgroundColor = ButtonColor1,
textColor = Color.Black,
onClickAction = {
launcher.launch("image/*")
})
PlaceholderInputField(label = "Никнейм", isSingleLine = true,
startValue = login.value, onTextChanged = { newLogin ->
login.value = newLogin
})
PlaceholderInputField(label = "Пароль", isSingleLine = true,
startValue = password.value, onTextChanged = { newPassword ->
password.value = newPassword
})
PlaceholderInputField(label = "Почта", isSingleLine = true,
startValue = email.value, onTextChanged = { newEmail ->
email.value = newEmail
})
ActiveButton(
label = "Сохранить",
backgroundColor = ButtonColor1,
textColor = Color.Black,
onClickAction = {
userViewModel.updateUser(
User(
id = userId.value,
login = login.value,
password = password.value,
email = email.value,
photo = photo.value
)
)
})
ActiveButton(label = "Назад", backgroundColor = ButtonColor2, textColor = Color.White,
onClickAction = {
navController.navigate("settings")
})
}
}
}

View File

@ -1,5 +1,6 @@
package com.example.mobileapp.screens
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@ -13,17 +14,20 @@ 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.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.example.mobileapp.GlobalUser
import com.example.mobileapp.R
import com.example.mobileapp.api.ApiStatus
import com.example.mobileapp.components.ActiveButton
import com.example.mobileapp.components.NavigationButton
import com.example.mobileapp.components.PasswordInputField
import com.example.mobileapp.components.PlaceholderInputField
import com.example.mobileapp.database.entities.User
import com.example.mobileapp.database.viewmodels.LoadingScreen
import com.example.mobileapp.database.viewmodels.MobileAppViewModelProvider
import com.example.mobileapp.database.viewmodels.UserViewModel
import com.example.mobileapp.ui.theme.ButtonColor1
@ -34,11 +38,15 @@ fun Registration(navController: NavHostController,
userViewModel: UserViewModel = viewModel(
factory = MobileAppViewModelProvider.Factory
)) {
val isRegistrated = remember { mutableStateOf(false) }
if(GlobalUser.getInstance().getUser() != null && !isRegistrated.value) {
isRegistrated.value = !isRegistrated.value
navController.navigate("main")
val context = LocalContext.current
when(userViewModel.apiStatus){
ApiStatus.DONE -> {
navController.navigate("main")
userViewModel.clearStatus()
}
ApiStatus.LOADING -> LoadingScreen(ButtonColor2)
ApiStatus.ERROR -> Toast.makeText(context, "Не удалось создать пользователя: " + userViewModel.apiError, Toast.LENGTH_SHORT).show()
else -> {}
}
val login = remember { mutableStateOf("") }
@ -46,45 +54,56 @@ fun Registration(navController: NavHostController,
val password = remember { mutableStateOf("") }
val repeatepassword = remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp),
verticalArrangement = Arrangement.Bottom
) {
Image(
painter = painterResource(id = R.drawable.registration),
contentDescription = "registration",
contentScale = ContentScale.Crop,
if(userViewModel.apiStatus != ApiStatus.LOADING) {
Column(
modifier = Modifier
.size(320.dp)
.padding(8.dp)
.align(Alignment.CenterHorizontally))
PlaceholderInputField(label = "Логин", isSingleLine = true, onTextChanged = {newlogin ->
login.value = newlogin
})
PlaceholderInputField(label = "Email", isSingleLine = true, onTextChanged = {newemail ->
email.value = newemail
})
PasswordInputField(label = "Пароль", onPasswordChanged = {newpassword ->
password.value = newpassword
})
PasswordInputField(label = "Пароль ещё раз", onPasswordChanged = {newpassword ->
repeatepassword.value = newpassword
})
ActiveButton(label = "Зарегистрироваться", backgroundColor = ButtonColor2,
textColor = Color.White, onClickAction = {
if (password.value == repeatepassword.value){
userViewModel.regUser(
User(
login = login.value,
password = password.value,
email = email.value
.fillMaxSize()
.padding(bottom = 8.dp),
verticalArrangement = Arrangement.Bottom
) {
Image(
painter = painterResource(id = R.drawable.registration),
contentDescription = "registration",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(320.dp)
.padding(8.dp)
.align(Alignment.CenterHorizontally)
)
PlaceholderInputField(
label = "Логин",
isSingleLine = true,
onTextChanged = { newlogin ->
login.value = newlogin
})
PlaceholderInputField(
label = "Email",
isSingleLine = true,
onTextChanged = { newemail ->
email.value = newemail
})
PasswordInputField(label = "Пароль", onPasswordChanged = { newpassword ->
password.value = newpassword
})
PasswordInputField(label = "Пароль ещё раз", onPasswordChanged = { newpassword ->
repeatepassword.value = newpassword
})
ActiveButton(label = "Зарегистрироваться", backgroundColor = ButtonColor2,
textColor = Color.White, onClickAction = {
if (password.value == repeatepassword.value) {
userViewModel.regUser(
User(
login = login.value,
password = password.value,
email = email.value
)
)
)
}
})
NavigationButton(navController = navController, destination = "authorization",
label = "Назад", backgroundColor = ButtonColor1, textColor = Color.Black)
}
})
NavigationButton(
navController = navController, destination = "authorization",
label = "Назад", backgroundColor = ButtonColor1, textColor = Color.Black
)
}
}
}

View File

@ -12,6 +12,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
@ -37,6 +38,18 @@ fun ReportScreen(
.fillMaxWidth(),
verticalArrangement = Arrangement.Center
) {
Column(
modifier = Modifier
.fillMaxWidth(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Отчёт по публикациям:",
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(top = 16.dp, bottom = 16.dp, start = 8.dp, end = 8.dp))
}
if(reportViewModel.report.value == null) {
DatePicker(startValue = dateFrom.value, onDateSelected = { newDateFrom ->
dateFrom.value = newDateFrom
@ -50,13 +63,9 @@ fun ReportScreen(
})
}
else{
Text(text = "Отчёт по публикациям иллюстраций",
fontSize = 28.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(8.dp))
Text(text = "Дата с ${dateFormat.format(reportViewModel.report.value?.dateFrom?.let { Date(it) })}",
fontSize = 20.sp,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(8.dp))