From aa78bfc14bf0fb602643def8296315ac3b0b0525 Mon Sep 17 00:00:00 2001 From: m-kuchergin Date: Wed, 25 Feb 2026 14:19:49 +0300 Subject: [PATCH] -add a password and login table -disallow screenshots --- .../java/ru/myitschool/work/core/TestIds.kt | 3 +- .../work/data/repo/AuthRepository.kt | 8 ++--- .../work/data/source/NetworkDataSource.kt | 4 +-- .../auth/CheckAndSaveAuthCodeUseCase.kt | 15 --------- .../auth/CheckAndSaveAuthDataUseCase.kt | 16 ++++++++++ .../domain/auth/CheckCodeFormatUseCase.kt | 12 ------- .../domain/auth/CheckLoginFormatUseCase.kt | 14 +++++++++ .../domain/auth/CheckPasswordFormatUseCase.kt | 24 ++++++++++++++ .../myitschool/work/ui/root/RootActivity.kt | 3 ++ .../work/ui/screen/auth/AuthIntent.kt | 4 +-- .../work/ui/screen/auth/AuthScreen.kt | 31 ++++++++++++++----- .../work/ui/screen/auth/AuthViewModel.kt | 19 ++++++++---- app/src/main/res/values/strings.xml | 3 +- 13 files changed, 106 insertions(+), 50 deletions(-) delete mode 100644 app/src/main/java/ru/myitschool/work/domain/auth/CheckAndSaveAuthCodeUseCase.kt create mode 100644 app/src/main/java/ru/myitschool/work/domain/auth/CheckAndSaveAuthDataUseCase.kt delete mode 100644 app/src/main/java/ru/myitschool/work/domain/auth/CheckCodeFormatUseCase.kt create mode 100644 app/src/main/java/ru/myitschool/work/domain/auth/CheckLoginFormatUseCase.kt create mode 100644 app/src/main/java/ru/myitschool/work/domain/auth/CheckPasswordFormatUseCase.kt diff --git a/app/src/main/java/ru/myitschool/work/core/TestIds.kt b/app/src/main/java/ru/myitschool/work/core/TestIds.kt index d67b884..048ea60 100644 --- a/app/src/main/java/ru/myitschool/work/core/TestIds.kt +++ b/app/src/main/java/ru/myitschool/work/core/TestIds.kt @@ -4,7 +4,8 @@ object TestIds { object Auth { const val ERROR = "auth_error" const val SIGN_BUTTON = "auth_sign_button" - const val CODE_INPUT = "auth_code_input" + const val LOGIN_INPUT = "auth_login_input" + const val PASSWORD_INPUT = "auth_password_input" } object Main { const val ERROR = "main_error" diff --git a/app/src/main/java/ru/myitschool/work/data/repo/AuthRepository.kt b/app/src/main/java/ru/myitschool/work/data/repo/AuthRepository.kt index e4126dd..978b310 100644 --- a/app/src/main/java/ru/myitschool/work/data/repo/AuthRepository.kt +++ b/app/src/main/java/ru/myitschool/work/data/repo/AuthRepository.kt @@ -16,13 +16,13 @@ object AuthRepository { private var codeCache: String? = null - suspend fun checkAndSave(text: String): Result { - return NetworkDataSource.checkAuth(text).onSuccess { success -> + suspend fun checkAndSave(textLogin: String, textPassword: String): Result { + return NetworkDataSource.checkAuth(textLogin, textPassword).onSuccess { success -> if (success) { - codeCache = text + codeCache = textLogin // TODO(переделать под отправку и логина и пароля в api) App.context.userDataStore.edit { preferences -> val prefKey = stringPreferencesKey(CODE_KEY) - preferences[prefKey] = text + preferences[prefKey] = textLogin // TODO(переделать под отправку и логина и пароля в api) } } } diff --git a/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt index 85387ac..c82aacd 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt @@ -36,9 +36,9 @@ object NetworkDataSource { } } - suspend fun checkAuth(code: String): Result = withContext(Dispatchers.IO) { + suspend fun checkAuth(login: String, password: String): Result = withContext(Dispatchers.IO) { return@withContext runCatching { - val response = client.get(getUrl(code, Constants.AUTH_URL)) + val response = client.post(getUrl(login, Constants.AUTH_URL)) when (response.status) { HttpStatusCode.OK -> true else -> false diff --git a/app/src/main/java/ru/myitschool/work/domain/auth/CheckAndSaveAuthCodeUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/auth/CheckAndSaveAuthCodeUseCase.kt deleted file mode 100644 index 012fb6f..0000000 --- a/app/src/main/java/ru/myitschool/work/domain/auth/CheckAndSaveAuthCodeUseCase.kt +++ /dev/null @@ -1,15 +0,0 @@ -package ru.myitschool.work.domain.auth - -import ru.myitschool.work.data.repo.AuthRepository - -class CheckAndSaveAuthCodeUseCase( - private val repository: AuthRepository -) { - suspend operator fun invoke( - text: String - ): Result { - return repository.checkAndSave(text).mapCatching { success -> - if (!success) error("Code is incorrect") - } - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/auth/CheckAndSaveAuthDataUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/auth/CheckAndSaveAuthDataUseCase.kt new file mode 100644 index 0000000..c4a11d7 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/auth/CheckAndSaveAuthDataUseCase.kt @@ -0,0 +1,16 @@ +package ru.myitschool.work.domain.auth + +import ru.myitschool.work.data.repo.AuthRepository + +class CheckAndSaveAuthDataUseCase( + private val repository: AuthRepository +) { + suspend operator fun invoke( + textLogin: String, + textPassword: String + ): Result { + return repository.checkAndSave(textLogin, textPassword).mapCatching { success -> + if (!success) error("Login or password is incorrect") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/auth/CheckCodeFormatUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/auth/CheckCodeFormatUseCase.kt deleted file mode 100644 index fe291a0..0000000 --- a/app/src/main/java/ru/myitschool/work/domain/auth/CheckCodeFormatUseCase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ru.myitschool.work.domain.auth - -class CheckCodeFormatUseCase { - operator fun invoke( - text: String - ): Boolean { - return text.length == 4 && text.all { char -> - char.isLetterOrDigit() && - ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z') || char.isDigit()) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/auth/CheckLoginFormatUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/auth/CheckLoginFormatUseCase.kt new file mode 100644 index 0000000..aaa2f47 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/auth/CheckLoginFormatUseCase.kt @@ -0,0 +1,14 @@ +package ru.myitschool.work.domain.auth + +class CheckLoginFormatUseCase { + operator fun invoke( + text: String + ): Boolean { + return text.all { char -> + (char.isLetterOrDigit() && + ((char in ('A'..'Z')) || (char in ('a'..'z')) || char.isDigit())) + || char == '.' + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/auth/CheckPasswordFormatUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/auth/CheckPasswordFormatUseCase.kt new file mode 100644 index 0000000..5ba3471 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/auth/CheckPasswordFormatUseCase.kt @@ -0,0 +1,24 @@ +package ru.myitschool.work.domain.auth + +import java.util.Locale +import java.util.Locale.getDefault + +class CheckPasswordFormatUseCase { + operator fun invoke( + textLogin: String, + textPassword: String + ): Boolean { + val lowerCasePassword = textPassword.lowercase(getDefault()) + val lowerCaseLogin = textLogin.lowercase(getDefault()) + val intersect = lowerCasePassword.toList().intersect(lowerCaseLogin.toList()); + return !(textPassword.length < 8 || !textPassword.all { char -> + char.isLetterOrDigit() + && ((char in 'A'..'Z') + || (char in 'a'..'z') + || char.isDigit()) + && textPassword.count { it == char } < 3 + && intersect.size < 3 + + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/root/RootActivity.kt b/app/src/main/java/ru/myitschool/work/ui/root/RootActivity.kt index 54b156d..beeceab 100644 --- a/app/src/main/java/ru/myitschool/work/ui/root/RootActivity.kt +++ b/app/src/main/java/ru/myitschool/work/ui/root/RootActivity.kt @@ -1,6 +1,7 @@ package ru.myitschool.work.ui.root import android.os.Bundle +import android.view.WindowManager import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -15,6 +16,8 @@ class RootActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + setContent { WorkTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthIntent.kt b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthIntent.kt index 74f200a..93ca75f 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthIntent.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthIntent.kt @@ -1,6 +1,6 @@ package ru.myitschool.work.ui.screen.auth sealed interface AuthIntent { - data class Send(val text: String): AuthIntent - data class TextInput(val text: String): AuthIntent + data class Send(val textLogin: String, val textPassword: String): AuthIntent + data class TextInput(val textLogin: String, val textPassword: String): AuthIntent } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthScreen.kt b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthScreen.kt index 8c54884..b202168 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthScreen.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthScreen.kt @@ -77,22 +77,39 @@ private fun Content( viewModel: AuthViewModel, state: AuthState.Data ) { - var inputText by rememberSaveable { mutableStateOf("") } + var inputLogin by rememberSaveable { mutableStateOf("") } Spacer(modifier = Modifier.size(16.dp)) TextField( - modifier = Modifier.testTag(TestIds.Auth.CODE_INPUT).fillMaxWidth(), - value = inputText, + modifier = Modifier.testTag(TestIds.Auth.LOGIN_INPUT).fillMaxWidth(), + value = inputLogin, onValueChange = { - inputText = it - viewModel.onIntent(AuthIntent.TextInput(it)) + inputLogin = it + viewModel.onIntent(AuthIntent.TextInput( + it, + textPassword = "" + )) }, - label = { Text(stringResource(R.string.auth_label)) } + label = { Text(stringResource(R.string.auth_label_login)) } + ) + var inputPassword by rememberSaveable { mutableStateOf("") } + Spacer(modifier = Modifier.size(16.dp)) + TextField( + modifier = Modifier.testTag(TestIds.Auth.PASSWORD_INPUT).fillMaxWidth(), + value = inputPassword, + onValueChange = { + inputPassword = it + viewModel.onIntent(AuthIntent.TextInput( + inputLogin, + textPassword = it + )) + }, + label = { Text(stringResource(R.string.auth_label_password)) } ) Spacer(modifier = Modifier.size(16.dp)) Button( modifier = Modifier.testTag(TestIds.Auth.SIGN_BUTTON).fillMaxWidth(), onClick = { - viewModel.onIntent(AuthIntent.Send(inputText)) + viewModel.onIntent(AuthIntent.Send(inputLogin, inputPassword)) }, enabled = state.isEnabledSend ) { diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt index c28f5cd..4886d3b 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/auth/AuthViewModel.kt @@ -10,13 +10,17 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import ru.myitschool.work.data.repo.AuthRepository -import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase -import ru.myitschool.work.domain.auth.CheckCodeFormatUseCase +import ru.myitschool.work.domain.auth.CheckAndSaveAuthDataUseCase +import ru.myitschool.work.domain.auth.CheckLoginFormatUseCase +import ru.myitschool.work.domain.auth.CheckPasswordFormatUseCase import ru.myitschool.work.ui.nav.MainScreenDestination class AuthViewModel : ViewModel() { - private val checkCodeFormatUseCase by lazy { CheckCodeFormatUseCase() } - private val checkAndSaveAuthCodeUseCase by lazy { CheckAndSaveAuthCodeUseCase(AuthRepository) } + private val checkLoginFormatUseCase by lazy { CheckLoginFormatUseCase() } + private val checkAndSaveAuthDataUseCase by lazy { CheckAndSaveAuthDataUseCase(AuthRepository) } + private val checkPasswordFormatUseCase by lazy { CheckPasswordFormatUseCase() } + + private val _uiState = MutableStateFlow( AuthState.Data( isEnabledSend = false, @@ -32,7 +36,7 @@ class AuthViewModel : ViewModel() { when (intent) { is AuthIntent.Send -> { viewModelScope.launch { - checkAndSaveAuthCodeUseCase.invoke(intent.text).fold( + checkAndSaveAuthDataUseCase.invoke(intent.textLogin, intent.textPassword).fold( onSuccess = { _actionFlow.emit(AuthAction.Open(MainScreenDestination)) }, @@ -46,10 +50,13 @@ class AuthViewModel : ViewModel() { ) } } + is AuthIntent.TextInput -> { updateStateIfData { oldState -> oldState.copy( - isEnabledSend = checkCodeFormatUseCase.invoke(intent.text), + isEnabledSend = (checkLoginFormatUseCase.invoke(intent.textLogin) && checkPasswordFormatUseCase.invoke( + intent.textLogin, intent.textPassword + )), error = null ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6450ed4..2449e7c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,7 +2,8 @@ Work Приложение Привет! Введи код для авторизации - Код + Логин + Пароль Войти Обновить