Внедрение аунтификации
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
package ru.megboyzz.dnevnik
|
||||
|
||||
import android.accounts.Account
|
||||
import android.app.Application
|
||||
import androidx.room.Room
|
||||
import ru.megboyzz.dnevnik.db.CredentialsDataBase
|
||||
|
||||
class App : Application() {
|
||||
var database: CredentialsDataBase? = null
|
||||
|
||||
lateinit var database: CredentialsDataBase
|
||||
private set
|
||||
|
||||
override fun onCreate() {
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package ru.megboyzz.dnevnik
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.ReportFragment.Companion.reportFragment
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -15,37 +20,19 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import org.jsoup.Jsoup
|
||||
import ru.megboyzz.dnevnik.navigation.AppNavHost
|
||||
import ru.megboyzz.dnevnik.ui.theme.lightGray
|
||||
import ru.megboyzz.dnevnik.viewmodel.AuthorizationViewModel
|
||||
import ru.megboyzz.dnevnik.viewmodel.AuthorizationViewModelFactory
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
lateinit var app: App
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val login = "kitsekaterina"
|
||||
val password = "k12345"
|
||||
val returnUrl =
|
||||
"https://login.dnevnik.ru/oauth2?response_type=token&client_id=bb97b3e445a340b9b9cab4b9ea0dbd6f&scope=CommonInfo,ContactInfo,FriendsAndRelatives,EducationalInfo"
|
||||
|
||||
val baseUrl = "https://login.dnevnik.ru/login/"
|
||||
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
val execute: org.jsoup.Connection.Response = Jsoup
|
||||
.connect(baseUrl)
|
||||
.method(org.jsoup.Connection.Method.POST)
|
||||
.data("ReturnUrl", returnUrl)
|
||||
.data("login", login)
|
||||
.data("password", password)
|
||||
.followRedirects(true).execute()
|
||||
val url: URL = execute.url()
|
||||
|
||||
val httpUrl = url.toString().toHttpUrlOrNull()
|
||||
|
||||
val queryParameter = httpUrl?.queryParameter("result")
|
||||
|
||||
println("$httpUrl $queryParameter")
|
||||
}
|
||||
this.app = application as App
|
||||
|
||||
setContent {
|
||||
|
||||
@@ -57,4 +44,19 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
|
||||
val credentialsDao = app.database.credentialsDao()
|
||||
val credentials = credentialsDao.getCredentials()
|
||||
if(!credentials.rememberMe)
|
||||
credentialsDao.deleteCredentials()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,12 @@ import com.kizitonwose.calendar.compose.CalendarState
|
||||
import com.kizitonwose.calendar.compose.weekcalendar.WeekCalendarLayoutInfo
|
||||
import com.kizitonwose.calendar.compose.weekcalendar.WeekCalendarState
|
||||
import com.kizitonwose.calendar.core.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import ru.megboyzz.dnevnik.navigation.BaseNavRote
|
||||
import ru.megboyzz.dnevnik.ui.theme.dark
|
||||
import java.time.DayOfWeek
|
||||
import kotlin.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun Int.AsPainter() = painterResource(this)
|
||||
@@ -201,16 +203,28 @@ fun CalendarDay.week(): Week{
|
||||
|
||||
}
|
||||
|
||||
fun WeekDay.week(): Week{
|
||||
val weekList = mutableListOf<WeekDay>()
|
||||
val dayOrder = this.date.dayOfWeek.ordinal
|
||||
var mondayLocalDate = this.date.minusDays(dayOrder.toLong())
|
||||
|
||||
DayOfWeek.values().forEach {
|
||||
weekList.add(WeekDay(mondayLocalDate, WeekDayPosition.RangeDate))
|
||||
mondayLocalDate = mondayLocalDate.plusDays(1)
|
||||
}
|
||||
return Week(weekList)
|
||||
private class MutableStateAdapter<T>(
|
||||
private val state: State<T>,
|
||||
private val mutate: (T) -> Unit
|
||||
) : MutableState<T> {
|
||||
|
||||
override var value: T
|
||||
get() = state.value
|
||||
set(value) {
|
||||
mutate(value)
|
||||
}
|
||||
|
||||
override fun component1(): T = value
|
||||
override fun component2(): (T) -> Unit = { value = it }
|
||||
}
|
||||
|
||||
// Flow
|
||||
@Composable
|
||||
fun <T> MutableStateFlow<T>.collectAsMutableState(
|
||||
context: CoroutineContext = EmptyCoroutineContext
|
||||
): MutableState<T> = MutableStateAdapter(
|
||||
state = collectAsState(context),
|
||||
mutate = { value = it }
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package ru.megboyzz.dnevnik.authorization
|
||||
|
||||
import android.util.Log
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import org.jsoup.Connection
|
||||
import org.jsoup.Jsoup
|
||||
import ru.megboyzz.dnevnik.db.dao.CredentialsDao
|
||||
import ru.megboyzz.dnevnik.entities.db.Credentials
|
||||
import java.net.URL
|
||||
|
||||
|
||||
class AuthorizationManager(private val credentialsDao: CredentialsDao) {
|
||||
|
||||
private val returnUrl =
|
||||
"https://login.dnevnik.ru/oauth2?response_type=token&client_id=bb97b3e445a340b9b9cab4b9ea0dbd6f&scope=CommonInfo,ContactInfo,FriendsAndRelatives,EducationalInfo"
|
||||
|
||||
private val baseUrl = "https://login.dnevnik.ru/login/"
|
||||
|
||||
private var credentials = Credentials(token = "", cookies = mapOf(), rememberMe = false)
|
||||
|
||||
fun login(
|
||||
login: String,
|
||||
password: String,
|
||||
rememberMe: Boolean
|
||||
): AuthorizationStatus {
|
||||
|
||||
val execute = runCatching<Connection.Response> {
|
||||
Jsoup
|
||||
.connect(baseUrl)
|
||||
.method(Connection.Method.POST)
|
||||
.data("ReturnUrl", returnUrl)
|
||||
.data("login", login)
|
||||
.data("password", password)
|
||||
.followRedirects(true)
|
||||
.execute()
|
||||
}.onFailure {
|
||||
|
||||
Log.i("AuthorizationManager", "No internet")
|
||||
return AuthorizationStatus.NO_INTERNET
|
||||
}
|
||||
|
||||
val response = execute.getOrNull()
|
||||
|
||||
if(response != null){
|
||||
|
||||
if(response.statusCode() != 200) {
|
||||
Log.i("AuthorizationManager", "Maintain")
|
||||
return AuthorizationStatus.MAINTAIN
|
||||
}
|
||||
|
||||
val url = response.url()
|
||||
|
||||
if(url.getResult() != "success") {
|
||||
Log.i("AuthorizationManager", "Wrong login")
|
||||
return AuthorizationStatus.WRONG_CREDENTIALS
|
||||
}
|
||||
|
||||
//Безопасная зона
|
||||
val cookies = response.cookies()
|
||||
credentials = Credentials(
|
||||
token = url.getToken(),
|
||||
cookies = cookies,
|
||||
rememberMe = rememberMe
|
||||
)
|
||||
dbUpdate()
|
||||
|
||||
}else return AuthorizationStatus.NO_INTERNET //Может быть опасно
|
||||
|
||||
return AuthorizationStatus.LOGGED
|
||||
|
||||
}
|
||||
|
||||
fun checkCredentials(): AuthorizationStatus{
|
||||
|
||||
val credentialsIsExists = credentialsDao.credentialsIsExists()
|
||||
return if(credentialsIsExists)
|
||||
AuthorizationStatus.LOGGED
|
||||
else
|
||||
AuthorizationStatus.NOTHING
|
||||
|
||||
}
|
||||
|
||||
fun dbUpdate(){
|
||||
credentialsDao.setCredentials(credentials)
|
||||
}
|
||||
|
||||
fun deleteCredentials(){
|
||||
credentialsDao.deleteCredentials()
|
||||
}
|
||||
|
||||
fun URL.getResult(): String{
|
||||
|
||||
val httpUrl = this.toString().toHttpUrlOrNull() ?: return ""
|
||||
|
||||
return httpUrl.queryParameter("result") ?: ""
|
||||
|
||||
}
|
||||
|
||||
fun URL.getToken(): String{
|
||||
|
||||
val tokenRegex = "access_token=([A-Za-z0-9]+)"
|
||||
val regex = Regex(tokenRegex)
|
||||
val find = regex.find(this.toString())
|
||||
if(find != null)
|
||||
return find.groupValues[1]
|
||||
return ""
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.megboyzz.dnevnik.authorization
|
||||
|
||||
enum class AuthorizationStatus {
|
||||
|
||||
LOGGED,
|
||||
LOGGING,
|
||||
WRONG_CREDENTIALS,
|
||||
NO_INTERNET,
|
||||
MAINTAIN,
|
||||
NOTHING
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ru.megboyzz.dnevnik.db.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
|
||||
class CookiesConverter {
|
||||
|
||||
private val gson = Gson()
|
||||
private val mapType = object : TypeToken<Map<String, String>>() {}.type
|
||||
|
||||
@TypeConverter
|
||||
fun fromMap(cookies: Map<String, String>): String = gson.toJson(cookies)
|
||||
|
||||
@TypeConverter
|
||||
fun toMap(savedCookies: String): Map<String, String> = gson.fromJson(savedCookies, mapType)
|
||||
|
||||
}
|
||||
@@ -1,17 +1,22 @@
|
||||
package ru.megboyzz.dnevnik.db.dao
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import ru.megboyzz.dnevnik.entities.db.Credentials
|
||||
|
||||
@Dao
|
||||
interface CredentialsDao {
|
||||
|
||||
@Query("SELECT * FROM Credentials WHERE id = 1")
|
||||
fun getCredentials(): LiveData<Credentials>
|
||||
|
||||
@Query("DELETE FROM Credentials WHERE id = 1")
|
||||
@Query("SELECT EXISTS(SELECT * FROM Credentials WHERE id = 0)")
|
||||
fun credentialsIsExists(): Boolean
|
||||
@Query("SELECT * FROM Credentials WHERE id = 0")
|
||||
fun getCredentials(): Credentials
|
||||
|
||||
@Query("DELETE FROM Credentials WHERE id = 0")
|
||||
fun deleteCredentials()
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
package ru.megboyzz.dnevnik.entities.db
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
import ru.megboyzz.dnevnik.db.converters.CookiesConverter
|
||||
|
||||
@Entity
|
||||
@TypeConverters(CookiesConverter::class)
|
||||
data class Credentials(
|
||||
@PrimaryKey
|
||||
val id: Int = 0,
|
||||
val token: String,
|
||||
val cookies: Map<String, String>
|
||||
val cookies: Map<String, String>,
|
||||
val rememberMe: Boolean
|
||||
)
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
package ru.megboyzz.dnevnik.navigation
|
||||
|
||||
import android.window.SplashScreen
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.CubicBezierEasing
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.material.rememberScaffoldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.megboyzz.dnevnik.MainActivity
|
||||
import ru.megboyzz.dnevnik.authorization.AuthorizationStatus
|
||||
import ru.megboyzz.dnevnik.collectAsMutableState
|
||||
import ru.megboyzz.dnevnik.screens.*
|
||||
import ru.megboyzz.dnevnik.viewmodel.AuthorizationViewModel
|
||||
import ru.megboyzz.dnevnik.viewmodel.AuthorizationViewModelFactory
|
||||
|
||||
sealed class BaseNavRote(open val route: String)
|
||||
|
||||
@@ -29,18 +32,33 @@ sealed class AppNavRoute(override val route: String): BaseNavRote(route){
|
||||
object Settings: AppNavRoute("settings_screen")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun AppNavHost(){
|
||||
|
||||
val scaffoldState = rememberScaffoldState()
|
||||
val navController = rememberNavController()
|
||||
|
||||
val app = (LocalContext.current as MainActivity).app
|
||||
|
||||
val authorizationViewModel: AuthorizationViewModel = viewModel(
|
||||
factory = AuthorizationViewModelFactory(app)
|
||||
)
|
||||
|
||||
val loginStatus = authorizationViewModel.loginStatus.collectAsMutableState()
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = AppNavRoute.Login.route /* сделать проверку куки файлов */
|
||||
startDestination = AppNavRoute.Splash.route
|
||||
){
|
||||
composable(AppNavRoute.Splash.route) { }
|
||||
composable(AppNavRoute.Splash.route) {
|
||||
|
||||
if(loginStatus.value != AuthorizationStatus.LOGGED){
|
||||
navController.navigate(AppNavRoute.Login.route)
|
||||
}else
|
||||
navController.navigate(AppNavRoute.Marks.route)
|
||||
|
||||
|
||||
}
|
||||
composable(AppNavRoute.Login.route) { LoginScreen(navController) }
|
||||
composable(AppNavRoute.Marks.route) { MarksScreen(navController, scaffoldState) }
|
||||
composable(AppNavRoute.Schedule.route) { ScheduleScreen(navController, scaffoldState) }
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
package ru.megboyzz.dnevnik.repository
|
||||
|
||||
class AuthorizationRepository() {
|
||||
}
|
||||
@@ -1,35 +1,70 @@
|
||||
package ru.megboyzz.dnevnik.screens
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import ru.megboyzz.dnevnik.MainActivity
|
||||
import ru.megboyzz.dnevnik.authorization.AuthorizationStatus
|
||||
import ru.megboyzz.dnevnik.collectAsMutableState
|
||||
import ru.megboyzz.dnevnik.navigation.AppNavRoute
|
||||
import ru.megboyzz.dnevnik.screens.ui.AlertMessageBox
|
||||
import ru.megboyzz.dnevnik.screens.ui.LoginScreenContent
|
||||
import ru.megboyzz.dnevnik.viewmodel.AuthorizationViewModel
|
||||
import ru.megboyzz.dnevnik.viewmodel.AuthorizationViewModelFactory
|
||||
|
||||
//TODO Переверстать экран входа
|
||||
@Composable
|
||||
fun LoginScreen(navController: NavController) {
|
||||
|
||||
val login = remember {
|
||||
mutableStateOf("")
|
||||
}
|
||||
val password = remember {
|
||||
mutableStateOf("")
|
||||
}
|
||||
val isError = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val rememberMe = remember {
|
||||
mutableStateOf(false)
|
||||
val app = (LocalContext.current as MainActivity).app
|
||||
|
||||
val authorizationViewModel: AuthorizationViewModel = viewModel(
|
||||
factory = AuthorizationViewModelFactory(app)
|
||||
)
|
||||
|
||||
val login = authorizationViewModel.login.collectAsMutableState()
|
||||
|
||||
val password = authorizationViewModel.password.collectAsMutableState()
|
||||
|
||||
val loginStatus = authorizationViewModel.loginStatus.collectAsMutableState()
|
||||
|
||||
val rememberMe = authorizationViewModel.rememberMe.collectAsMutableState()
|
||||
|
||||
var isError by remember{
|
||||
mutableStateOf(true)
|
||||
}
|
||||
|
||||
if(loginStatus.value == AuthorizationStatus.WRONG_CREDENTIALS && isError) {
|
||||
AlertMessageBox(
|
||||
title = "Информация",
|
||||
alertText = "Неверный логин или пароль",
|
||||
onClick = { isError =! isError }
|
||||
)
|
||||
}
|
||||
|
||||
if(loginStatus.value == AuthorizationStatus.LOGGED)
|
||||
navController.navigate(AppNavRoute.Marks.route)
|
||||
|
||||
LoginScreenContent(
|
||||
login = login,
|
||||
password = password,
|
||||
isError = isError,
|
||||
isRememberMe = rememberMe,
|
||||
onSignIn = { navController.navigate(AppNavRoute.Marks.route) },
|
||||
login = login.value,
|
||||
onLoginChange = { login.value = it },
|
||||
|
||||
password = password.value,
|
||||
onPasswordChange = { password.value = it },
|
||||
|
||||
isError = loginStatus.value == AuthorizationStatus.WRONG_CREDENTIALS,
|
||||
onErrorChange = { },
|
||||
|
||||
isLoading = loginStatus.value == AuthorizationStatus.LOGGING,
|
||||
|
||||
isRememberMe = rememberMe.value,
|
||||
onRememberChange = { rememberMe.value = it },
|
||||
|
||||
onSignIn = {
|
||||
if(loginStatus.value != AuthorizationStatus.LOGGED)
|
||||
authorizationViewModel.login()
|
||||
},
|
||||
onSignUp = { /*TODO*/ },
|
||||
onForgetPassword = { /* TODO */ }
|
||||
)
|
||||
|
||||
@@ -33,7 +33,6 @@ import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import ru.megboyzz.dnevnik.*
|
||||
import ru.megboyzz.dnevnik.R
|
||||
import ru.megboyzz.dnevnik.ui.theme.*
|
||||
@@ -102,18 +101,20 @@ fun ProfileCard(
|
||||
|
||||
@Composable
|
||||
fun MainTextField(
|
||||
value: MutableState<String>,
|
||||
isError: MutableState<Boolean> = remember { mutableStateOf(false) },
|
||||
value: String,
|
||||
onChange: (String) -> Unit,
|
||||
isError: Boolean,
|
||||
isLoading: Boolean = false,
|
||||
onErrorChanged: (Boolean) -> Unit,
|
||||
label: String = "",
|
||||
keyboardType: KeyboardType = KeyboardType.Text,
|
||||
onChange: (it: String) -> Unit = { value.value = it }
|
||||
) {
|
||||
|
||||
var passwordVisible by rememberSaveable { mutableStateOf(false) }
|
||||
val focusRequester = FocusRequester()
|
||||
|
||||
OutlinedTextField(
|
||||
value = value.value,
|
||||
value = value,
|
||||
onValueChange = onChange,
|
||||
label = {
|
||||
Text(
|
||||
@@ -126,8 +127,8 @@ fun MainTextField(
|
||||
textStyle = H2,
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
textColor = dark,
|
||||
unfocusedBorderColor = if (isError.value) falseColor else mainBlue,
|
||||
focusedBorderColor = if (isError.value) falseColor else mainBlue,
|
||||
unfocusedBorderColor = if (isError) falseColor else mainBlue,
|
||||
focusedBorderColor = if (isError) falseColor else mainBlue,
|
||||
focusedLabelColor = dark,
|
||||
unfocusedLabelColor = dark,
|
||||
cursorColor = dark
|
||||
@@ -152,42 +153,50 @@ fun MainTextField(
|
||||
IconButton(onClick = { passwordVisible = !passwordVisible }) {
|
||||
Icon(imageVector = image, "")
|
||||
}
|
||||
} else {
|
||||
if(isLoading) LoginProgress()
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester)
|
||||
.onFocusChanged {
|
||||
if (it.isFocused)
|
||||
if (isError.value) isError.value = false
|
||||
if (isError) onErrorChanged(false)
|
||||
}
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoginProgress() {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
strokeWidth = 2.dp,
|
||||
color = mainBlue
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainCheckbox(
|
||||
isChecked: MutableState<Boolean>,
|
||||
isChecked: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
size: Float = 24f,
|
||||
checkedColor: Color = mainBlue,
|
||||
uncheckedColor: Color = lightGray,
|
||||
checkmarkColor: Color = white,
|
||||
onValueChange: () -> Unit
|
||||
onValueChange: (Boolean) -> Unit
|
||||
) {
|
||||
|
||||
val checkboxColor: Color by animateColorAsState(if (isChecked.value) checkedColor else uncheckedColor)
|
||||
val checkboxColor: Color by animateColorAsState(if (isChecked) checkedColor else uncheckedColor)
|
||||
val density = LocalDensity.current
|
||||
val duration = 200
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.toggleable(
|
||||
value = isChecked.value,
|
||||
value = isChecked,
|
||||
role = Role.Checkbox,
|
||||
onValueChange = {
|
||||
isChecked.value = !isChecked.value
|
||||
onValueChange.invoke()
|
||||
}
|
||||
onValueChange = onValueChange
|
||||
)
|
||||
) {
|
||||
Card(
|
||||
@@ -202,7 +211,7 @@ fun MainCheckbox(
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
androidx.compose.animation.AnimatedVisibility(
|
||||
visible = isChecked.value,
|
||||
visible = isChecked,
|
||||
enter = slideInHorizontally(
|
||||
animationSpec = tween(duration)
|
||||
) {
|
||||
@@ -618,7 +627,8 @@ fun AlertMessageBox(
|
||||
contentColor = white
|
||||
),
|
||||
modifier = Modifier
|
||||
.defaultMinSize(170.dp).padding(bottom = 30.dp)
|
||||
.defaultMinSize(170.dp)
|
||||
.padding(bottom = 30.dp)
|
||||
) {
|
||||
Text(
|
||||
text = R.string.title_ok.AsString(),
|
||||
|
||||
@@ -22,10 +22,15 @@ import ru.megboyzz.dnevnik.R
|
||||
|
||||
@Composable
|
||||
fun LoginScreenContent(
|
||||
login: MutableState<String>,
|
||||
password: MutableState<String>,
|
||||
isError: MutableState<Boolean>,
|
||||
isRememberMe: MutableState<Boolean>,
|
||||
login: String,
|
||||
onLoginChange: (String) -> Unit,
|
||||
password: String,
|
||||
onPasswordChange: (String) -> Unit,
|
||||
isLoading: Boolean,
|
||||
isError: Boolean,
|
||||
onErrorChange: (Boolean) -> Unit,
|
||||
isRememberMe: Boolean,
|
||||
onRememberChange: (Boolean) -> Unit,
|
||||
onSignIn: () -> Unit,
|
||||
onSignUp: () -> Unit,
|
||||
onForgetPassword: () -> Unit
|
||||
@@ -65,14 +70,19 @@ fun LoginScreenContent(
|
||||
MainTextField(
|
||||
value = login,
|
||||
label = R.string.title_login.AsString(),
|
||||
isError = isError
|
||||
isError = isError,
|
||||
isLoading = isLoading,
|
||||
onErrorChanged = onErrorChange,
|
||||
onChange = onLoginChange
|
||||
)
|
||||
SpacerHeight(15.dp)
|
||||
MainTextField(
|
||||
value = password,
|
||||
keyboardType = KeyboardType.Password,
|
||||
label = R.string.title_password.AsString(),
|
||||
isError = isError
|
||||
isError = isError,
|
||||
onErrorChanged = onErrorChange,
|
||||
onChange = onPasswordChange
|
||||
)
|
||||
}
|
||||
SpacerHeight(5.dp)
|
||||
@@ -87,7 +97,10 @@ fun LoginScreenContent(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
MainCheckbox(isRememberMe) { }
|
||||
MainCheckbox(
|
||||
isChecked = isRememberMe,
|
||||
onValueChange = onRememberChange
|
||||
)
|
||||
SpacerWidth(5.dp)
|
||||
Text(
|
||||
text = R.string.title_remember_me.AsString(),
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package ru.megboyzz.dnevnik.service
|
||||
|
||||
enum class AuthorizationStatus {
|
||||
WRONG_CREDENTIALS,
|
||||
NO_INTERNET,
|
||||
MAINTAIN
|
||||
}
|
||||
@@ -1,36 +1,48 @@
|
||||
package ru.megboyzz.dnevnik.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.megboyzz.dnevnik.App
|
||||
import ru.megboyzz.dnevnik.authorization.AuthorizationManager
|
||||
import ru.megboyzz.dnevnik.authorization.AuthorizationStatus
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
//Если есть попадание в этот вьюмодел то нам нужна авторизация
|
||||
class AuthorizationViewModel(application: Application) : AndroidViewModel(application) {
|
||||
class AuthorizationViewModel(application: App) : AndroidViewModel(application) {
|
||||
|
||||
val login = MutableStateFlow("")
|
||||
val password = MutableStateFlow("")
|
||||
val isLogged = MutableStateFlow(false)
|
||||
val rememberMe = MutableStateFlow(false)
|
||||
|
||||
val loginStatus = MutableStateFlow(AuthorizationStatus.NOTHING)
|
||||
private val authManager = AuthorizationManager(application.database.credentialsDao())
|
||||
|
||||
init {
|
||||
|
||||
|
||||
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
loginStatus.emit(authManager.checkCredentials())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun login(){
|
||||
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
|
||||
loginStatus.emit(AuthorizationStatus.LOGGING)
|
||||
loginStatus.emit(authManager.login(login.value, password.value, rememberMe.value))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class AuthorizationViewModelFactory(private val application: Application) : ViewModelProvider.Factory {
|
||||
class AuthorizationViewModelFactory(private val application: App) : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(AuthorizationViewModel::class.java))
|
||||
|
||||
Reference in New Issue
Block a user