diff --git a/.gitignore b/.gitignore index 87525d7..aaf216a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.idea /.gradle -/build \ No newline at end of file +/build +/local.properties diff --git a/Client_v1.apk b/Client_v1.apk new file mode 100644 index 0000000..0a433b8 Binary files /dev/null and b/Client_v1.apk differ diff --git a/Design_v0.penpot b/Design_v0.penpot new file mode 100644 index 0000000..08b205d Binary files /dev/null and b/Design_v0.penpot differ diff --git a/Design_v1.penpot b/Design_v1.penpot new file mode 100644 index 0000000..08b205d Binary files /dev/null and b/Design_v1.penpot differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e2bfe56 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Penpot Доска +https://pp.sicampus.ru/#/workspace?team-id=14a6b474-d5fa-807f-8007-9f077cdd8786&file-id=14a6b474-d5fa-807f-8007-9f541769571d&page-id=14a6b474-d5fa-807f-8007-9f541769571e \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a70cefb..f30bf74 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -34,6 +34,7 @@ android { } dependencies { + implementation("androidx.compose.foundation:foundation:1.10.3") defaultComposeLibrary() implementation("androidx.datastore:datastore-preferences:1.1.7") implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0") @@ -47,4 +48,8 @@ dependencies { implementation("io.ktor:ktor-client-content-negotiation:$ktor") implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0") + implementation("androidx.compose.ui:ui-tooling-preview:1.10.3") + implementation("androidx.compose.runtime:runtime-tracing:1.10.3") + implementation("androidx.compose.runtime:runtime-tracing:1.10.3") + debugImplementation ("androidx.compose.ui:ui-tooling:1.10.3") } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/core/Constants.kt b/app/src/main/java/ru/myitschool/work/core/Constants.kt index cd36239..0aa5d18 100644 --- a/app/src/main/java/ru/myitschool/work/core/Constants.kt +++ b/app/src/main/java/ru/myitschool/work/core/Constants.kt @@ -1,8 +1,10 @@ package ru.myitschool.work.core object Constants { - const val HOST = "http://localhost:8090" + const val HOST = "http://10.0.0.14:49183" const val AUTH_URL = "/auth" + + //TODO заменить на /auth const val INFO_URL = "/info" const val BOOKING_URL = "/booking" const val BOOK_URL = "/book" 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/source/NetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt index 85387ac..07377df 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 @@ -1,14 +1,19 @@ package ru.myitschool.work.data.source +import android.util.Log import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.engine.cio.CIO import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.basicAuth import io.ktor.client.request.get +import io.ktor.client.request.header import io.ktor.client.request.post import io.ktor.client.request.setBody import io.ktor.client.statement.bodyAsText import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpHeaders.Authorization import io.ktor.http.HttpStatusCode import io.ktor.http.contentType import io.ktor.serialization.kotlinx.json.json @@ -19,6 +24,7 @@ import ru.myitschool.work.core.Constants import ru.myitschool.work.data.dto.PlaceDto import ru.myitschool.work.data.dto.BookRequestDto import ru.myitschool.work.data.dto.UserDto +import kotlin.io.encoding.Base64 object NetworkDataSource { private val client by lazy { @@ -36,9 +42,25 @@ object NetworkDataSource { } } - suspend fun checkAuth(code: String): Result = withContext(Dispatchers.IO) { + suspend fun checkAuth(text: String): Result = withContext(Dispatchers.IO) { + return@withContext runCatching { - val response = client.get(getUrl(code, Constants.AUTH_URL)) + /* + val response = client.post( + getUrl(Constants.AUTH_URL)) + */ + + val response = client.post( + urlString = "http://10.0.0.177:49183/api/auth" //getUrl(Constants.AUTH_URL) + ) { + header(key = Authorization, value = basicAuth(text.split(Regex(":"))[0], text.split(Regex(":"))[1])) + } + + +// "Basic ${Base64.encode(text.toByteArray(), )}" "Basic ${Base64.encode(text.toByteArray())}" + //val response = client.get(getUrl(Constants.AUTH_URL)) {} + Log.d("RESPONSE", response.toString()) + Log.d("RESPONSE_STATUS", response.status.toString()) when (response.status) { HttpStatusCode.OK -> true else -> false @@ -49,7 +71,7 @@ object NetworkDataSource { suspend fun getInfo(code: String): Result = withContext(Dispatchers.IO) { return@withContext runCatching { println("!!!!!!!!!!!!!! getInfo $code") - val response = client.get(getUrl(code, Constants.INFO_URL)) + val response = client.get(getUrl(Constants.INFO_URL)) if (response.status == HttpStatusCode.OK) { println("!!!!!!!!!!!!!! getInfo OK ${response.bodyAsText()}") response.body() @@ -62,7 +84,7 @@ object NetworkDataSource { suspend fun getBooking(code: String): Result>?> = withContext(Dispatchers.IO) { return@withContext runCatching { - val response = client.get(getUrl(code, Constants.BOOKING_URL)) + val response = client.get(getUrl(Constants.BOOKING_URL)) if (response.status == HttpStatusCode.OK) { response.body>>() } else { @@ -73,7 +95,7 @@ object NetworkDataSource { suspend fun addBook(code: String, data: BookRequestDto): Result = withContext(Dispatchers.IO) { return@withContext runCatching { - val response = client.post(getUrl(code, Constants.BOOK_URL)) { + val response = client.post(getUrl(Constants.BOOK_URL)) { contentType(ContentType.Application.Json) setBody(data) } @@ -85,5 +107,5 @@ object NetworkDataSource { } } - private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl" + private fun getUrl(targetUrl: String) = "${Constants.HOST}/api$targetUrl" } \ No newline at end of file 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..033c0b6 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/auth/CheckAndSaveAuthDataUseCase.kt @@ -0,0 +1,19 @@ +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..632164a --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/auth/CheckPasswordFormatUseCase.kt @@ -0,0 +1,20 @@ +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 -> + textPassword.count { it == char } < 3 + && intersect.size < 3 + }) && !("[A-Za-z0-9]+".toRegex().matches(textPassword)) + } +} \ 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..b738e0a 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 @@ -10,11 +11,17 @@ import androidx.compose.material3.Scaffold import androidx.compose.ui.Modifier import ru.myitschool.work.ui.screen.AppNavHost import ru.myitschool.work.ui.theme.WorkTheme +import android.view.View class RootActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); + // Hide the status bar. + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN + actionBar?.hide() + 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 4b91b98..32ccc86 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 @@ -3,21 +3,23 @@ package ru.myitschool.work.ui.screen.auth import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -28,8 +30,18 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController +import androidx.navigation.compose.rememberNavController import ru.myitschool.work.R import ru.myitschool.work.core.TestIds +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.material3.OutlinedTextField +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.unit.sp @Composable fun AuthScreen( @@ -38,6 +50,7 @@ fun AuthScreen( ) { val state by viewModel.uiState.collectAsState() + LaunchedEffect(Unit) { viewModel.actionFlow.collect { action -> when (action) { @@ -51,15 +64,11 @@ fun AuthScreen( Column( modifier = Modifier .fillMaxSize() - .padding(all = 24.dp), + .padding(all = 44.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { - Text( - text = stringResource(R.string.auth_title), - style = MaterialTheme.typography.headlineSmall, - textAlign = TextAlign.Center - ) + when (val currentState = state) { is AuthState.Data -> Content(viewModel, currentState) is AuthState.Loading -> { @@ -72,37 +81,108 @@ fun AuthScreen( } @Composable -private fun Content( +private fun Content + ( viewModel: AuthViewModel, state: AuthState.Data ) { - var inputText by remember { mutableStateOf("") } - Spacer(modifier = Modifier.size(16.dp)) - TextField( - modifier = Modifier.testTag(TestIds.Auth.CODE_INPUT).fillMaxWidth(), - value = inputText, - onValueChange = { - inputText = it - viewModel.onIntent(AuthIntent.TextInput(it)) - }, - label = { Text(stringResource(R.string.auth_label)) } + var inputLogin by rememberSaveable { mutableStateOf("") } + val googleSans = FontFamily( + Font(R.font.googlesans_regular, FontWeight.Medium, FontStyle.Normal) ) - Spacer(modifier = Modifier.size(16.dp)) + Spacer(modifier = Modifier.size(5.dp)) + Text( + text = stringResource(R.string.auth_title), + fontFamily = googleSans, + color = Color(0xFF284777), + fontSize = 35.sp, + textAlign = TextAlign.Center, + fontWeight = FontWeight.Medium, + fontStyle = FontStyle.Normal, + modifier = Modifier.fillMaxWidth(1f) + ) + Spacer(modifier = Modifier.height(5.dp)) + Text( + text = stringResource(R.string.auth_label_login), + fontFamily = googleSans, + color = Color(0xFF74777f), + textAlign = TextAlign.Start, + fontWeight = FontWeight.Medium, + fontStyle = FontStyle.Normal, + modifier = Modifier.fillMaxWidth(1f) + ) + Spacer(modifier = Modifier.size(10.dp)) + OutlinedTextField( + modifier = Modifier.testTag(TestIds.Auth.LOGIN_INPUT).fillMaxWidth(), + value = inputLogin, + shape = RoundedCornerShape(12.dp), + colors = TextFieldDefaults.colors(unfocusedContainerColor = Color(0xFFededf4)), + onValueChange = { + inputLogin = it + viewModel.onIntent(AuthIntent.TextInput( + it, + textPassword = "" + )) + } + ) + var inputPassword by rememberSaveable { mutableStateOf("") } + Spacer(modifier = Modifier.size(20.dp)) + Text( + text = stringResource(R.string.auth_label_password), + fontFamily = googleSans, + color = Color(0xFF74777f), + textAlign = TextAlign.Start, + fontWeight = FontWeight.Medium, + fontStyle = FontStyle.Normal, + modifier = Modifier.fillMaxWidth(1f) + ) + Spacer(modifier = Modifier.size(10.dp)) + OutlinedTextField( + modifier = Modifier.testTag(TestIds.Auth.PASSWORD_INPUT).fillMaxWidth(), + value = inputPassword, + shape = RoundedCornerShape(12.dp), + colors = TextFieldDefaults.colors(unfocusedContainerColor = Color(0xFFededf4)), + onValueChange = { + inputPassword = it + viewModel.onIntent(AuthIntent.TextInput( + inputLogin, + textPassword = it + )) + } + ) + Spacer(modifier = Modifier.size(35.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 ) { - Text(stringResource(R.string.auth_sign_in)) + Text( + text = "Войти", + modifier = Modifier.padding(10.dp), + fontFamily = googleSans, + fontWeight = FontWeight.Medium, + fontStyle = FontStyle.Normal, + fontSize = 18.sp + ) } if (state.error != null) { Text( modifier = Modifier.testTag(TestIds.Auth.ERROR), - text = state.error, + text = if (viewModel.incorrectAttemptNum >= 5) { + "Превышен лимит попыток входа" + } else { + state.error + }, style = MaterialTheme.typography.bodyMedium, color = Color.Red, ) } +} + +@Preview +@Composable +fun AuthView() { + AuthScreen(navController = rememberNavController()) } \ No newline at end of file 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..93c286a 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,19 +10,25 @@ 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, error = null ) ) + + var incorrectAttemptNum = 0 val uiState: StateFlow = _uiState.asStateFlow() private val _actionFlow: MutableSharedFlow = MutableSharedFlow() @@ -32,7 +38,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)) }, @@ -42,14 +48,20 @@ class AuthViewModel : ViewModel() { error = error.message ) } + if (error.message == "Login or password is incorrect") { + incorrectAttemptNum += 1; + } } ) } } + is AuthIntent.TextInput -> { updateStateIfData { oldState -> oldState.copy( - isEnabledSend = checkCodeFormatUseCase.invoke(intent.text), + isEnabledSend = (checkPasswordFormatUseCase.invoke( + intent.textLogin, intent.textPassword + ) && checkLoginFormatUseCase.invoke(intent.textLogin) && incorrectAttemptNum <= 4), error = null ) } diff --git a/app/src/main/res/font/googlesans_regular.ttf b/app/src/main/res/font/googlesans_regular.ttf new file mode 100644 index 0000000..cc37c3f Binary files /dev/null and b/app/src/main/res/font/googlesans_regular.ttf differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127..36c11ea 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,10 +1,10 @@ - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF + #415f91 + #565F71 + #33618D + #D6E3FF + #DAE2F9 + #44474E + #F3F3FA \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9273cf..f7893ec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,8 +1,9 @@ Work - RootActivity - Привет! Введи код для авторизации - Код + Приложение + Авторизация + Логин + Пароль Войти Обновить diff --git a/khBGPU.apk b/khBGPU.apk new file mode 100644 index 0000000..0a433b8 Binary files /dev/null and b/khBGPU.apk differ