day2_commit_1_xml_з123123213
This commit is contained in:
parent
3da4b99cc0
commit
8535cf6370
122
README.md
122
README.md
@ -1,121 +1 @@
|
||||
[](https://sicampus.ru/gitea/core/docs/src/branch/main/how-upload-project.md)
|
||||
|
||||
# НТО 2024. II отборочный этап. Командные задани — клиентская часть
|
||||
|
||||
## 📖 Предыстория
|
||||
В компании S контроль доступа в офис осуществляется с помощью СКУД (системы контроля управления доступом). На данный момент у каждого сотрудника компании есть карта-пропуск с NFC меткой. А у каждой входной двери есть считыватель с обеих сторон. При поднесении карты к считывателю, дверь открывается, а информация о времени входа или выхода сотрудника фиксируется в базе данных.
|
||||
Администрации компании S требуется мобильное приложение, как для рядовых сотрудников, так и для администрации с возможностью просмотра посещений и работой электронного пропуска как временной замены обычного (при помощи сканировании QR кода, который находится на считывателе). Поскольку в приложении есть возможность использовать телефон как пропуск - то к данному приложению повышенные требования к безопасности всех данных, находящихся внутри него.
|
||||
|
||||
|
||||
|
||||
## 📋 Системные требования
|
||||
|
||||
| **Параметр** | **Требование** |
|
||||
|-----------------------------|---------------------------------------|
|
||||
| **Минимальная версия Android** | 9.0 (API 28) |
|
||||
| **Целевая версия Android** | 14 (API 34) |
|
||||
| **Поддерживаемые устройства** | смартфоны, планшеты |
|
||||
| **Ориентация экранов** | портретная |
|
||||
| **Языки** | русский, английский |
|
||||
| **Разрешения** | доступ к интернету, камера (при необходимости) |
|
||||
|
||||
|
||||
|
||||
## 📱 Техническое задание
|
||||
Требуется разработать нативное мобильное приложение, которое будет содержать следующие экраны.
|
||||
|
||||
|
||||
### 1. Экран авторизации
|
||||
|
||||
> Данный экран должен быть показан при первом входе в приложение, а также в ситуациях, когда пользователь не зарегистрировался в приложении.
|
||||
|
||||
#### Элементы, которые должны присутствовать на экране:
|
||||
- Поле ввода (`id/username`), в котором пользователю необходимо ввести свой логин.
|
||||
- Кнопка входа (`id/login`), по нажатию на которую пользователь авторизуется в системе.
|
||||
- По умолчанию скрытое (`GONE`) текстовое поле с ошибкой (`id/error`).
|
||||
|
||||
#### Требования к компонентам:
|
||||
1. В пустом поле ввода должна отображаться подсказка, что требуется ввести пользователю.
|
||||
2. Если хотя бы одно из условий ниже соблюдено - кнопка должна быть неактивной:
|
||||
- Поле ввода пустое.
|
||||
- Количество символов менее 3х.
|
||||
- Логин начинается с цифры.
|
||||
- Логин содержит символы, отличные от латинского алфавита и цифр.
|
||||
3. Поле ввода и кнопку должно быть видно при раскрытии клавиатуры.
|
||||
4. - При нажатии на кнопку входа необходимо проверить, что данный пользователь существует с помощью запроса `api/<LOGIN>/auth` (подробное описание представлено в техническом задании серверной части).
|
||||
5. В случае отсутствия логина или любой другой неполадки - необходимо вывести ошибку, пока пользователь не изменит текстовое поле или повторно не нажмёт на кнопку.
|
||||
6. После нажатия на кнопку - логин должен быть сохранён и при следующем открытии приложения экран авторизации не должен быть показан.
|
||||
7. После нажатия на кнопку - при нажатии стрелки назад - экран авторизации не должен быть показан повторно.
|
||||
8. Экран авторизации показывается только в случае, если пользователь неавторизован.
|
||||
|
||||
|
||||
|
||||
|
||||
### 2. Главный экран
|
||||
|
||||
> Данный экран содержит общую информацию о пользователе:
|
||||
>- ФИО
|
||||
>- Фото
|
||||
>- Должность
|
||||
>- Время последнего входа
|
||||
|
||||
#### Элементы, которые должны присутствовать на экране:
|
||||
- Текстовое поле (`id/fullname`), в котором написано имя пользователя.
|
||||
- Изображение (`id/photo`), на котором отображено фото пользователя.
|
||||
- Текстовое поле (`id/position`), в котором написана должность пользователя.
|
||||
- Текстовое поле (`id/lastEntry`), в котором написана дата и время последнего входа пользователя.
|
||||
- Кнопка (`id/logout`) для выхода пользователя из аккаунта.
|
||||
- Кнопка (`id/refresh`) для обновления данных.
|
||||
- Кнопка (`id/scan`) для сканирования QR кода.
|
||||
- По умолчанию скрытое текстовое поле с ошибкой (`id/error`).
|
||||
|
||||
#### Требования к компонентам:
|
||||
- В случае любой ошибки необходимо скрыть все элементы, кроме текстового поля с ошибкой и кнопки обновления данных.
|
||||
- Для получения данных необходимо использовать сетевой запрос `/api/<LOGIN>/info`.
|
||||
- Формат даты и времени последнего входа пользователя: `yyyy-MM-dd HH:mm` (например: 2024-02-31 08:31). Время необходимо отображать с сервера, без поправок на часовой пояс или локальное смещение.
|
||||
- При нажатии на кнопку выход все данные (если есть) пользователя должны быть очищены, а приложение должно открыть экран авторизации.
|
||||
- При нажатии кнопки сканирования необходимо открыть экран сканирования QR кода.
|
||||
- При нажатии на кнопку обновления данных - необходимо повторно вызывать сетевой запрос для получения актуальных данных.
|
||||
|
||||
|
||||
|
||||
### 3. Экран сканирования QR-кода
|
||||
|
||||
> Данный экран позволяет отсканировать код на турникете и войти с помощью смартфона. В данном случае данный экран будет уже написан и представлен dам в готовом виде в заготовке. Вам необходимо только подписаться на его результат с помощью **Result API** и обработать считанные данные из QR кода. **Данный экран нельзя модифицировать. Он поставляется как есть.**
|
||||
|
||||
|
||||
|
||||
### 4. Экран с результатом сканирования QR кода
|
||||
|
||||
> На данном экране необходимо вывести успешность или неуспешность входа с помощью метода QR кода.
|
||||
|
||||
#### Элементы, которые должны присутствовать на экране:
|
||||
- Текстовое поле (`id/result`), где содержится текст об успешности или неуспешности входа.
|
||||
- Кнопка (`id/close`) для закрытия данного экрана.
|
||||
|
||||
#### Требования к компонентам:
|
||||
- В случае, если результат пришёл пустым или со статусом “Отмена” - необходимо вывести пользователю текст:
|
||||
*"Вход был отменён/Operation was cancelled"*
|
||||
- В случае, если данные пришли, то необходимо их отправить на сервер с запросом `api/<LOGIN>/open`, добавив данные из результата и получить ответ.
|
||||
- Если сервер ответил успешно - то отображаем текст:
|
||||
*"Успешно/Success"*
|
||||
- Если сервер ответил любой ошибкой - то отображаем текст:
|
||||
*"Что-то пошло не так/Something wrong"*
|
||||
- Кнопка закрытия всегда открывает главный экран.
|
||||
|
||||
|
||||
|
||||
## 🛠 Решение
|
||||
|
||||
Необходимо загрузить свое решение в систему [по ссылке](https://innovationcampus.ru/lms/mod/quiz/view.php?id=2149).
|
||||
|
||||
Отметим, что работу необходимо осуществлять в представленных проектах-заготовках (шаблонах).
|
||||
|
||||
|
||||
|
||||
## ✅ Особенности оценивания
|
||||
|
||||
Оценивание происходит с помощью автоматической системы тестирования, которая в автоматическом режиме находит элементы и взаимодействует с ними (именно для этого у каждого элемента указан уникальный идентификатор, по которому будет производится поиск). Каждый тест происходит с чистой установки приложения.
|
||||
В случае тестирования сервера на него поочередно отправляются команды, описанные в API и ожидаются определенные корректные ответы.
|
||||
Сервер и приложение тестируются независимо.
|
||||
|
||||
Figma: https://www.figma.com/design/v9YlfUjxz6ChHS5mNWSQPN/TheDevs_Final?node-id=2-3&t=Hnk1mGCVo7joisAC-1
|
@ -1,9 +1,9 @@
|
||||
plugins {
|
||||
kotlinAndroid
|
||||
androidApplication
|
||||
jetbrainsKotlinSerialization version Version.Kotlin.language
|
||||
kotlinAnnotationProcessor
|
||||
id("com.google.dagger.hilt.android").version("2.51.1")
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
id("kotlin-kapt") // Добавлено для KAPT
|
||||
id("dagger.hilt.android.plugin") // Используйте этот синтаксис для Hilt
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version Version.Kotlin.language // Убедитесь, что версия актуальна
|
||||
}
|
||||
|
||||
val packageName = "ru.myitschool.work"
|
||||
@ -22,9 +22,9 @@ android {
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildFeatures.viewBinding = true
|
||||
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = Version.Kotlin.javaSource
|
||||
@ -37,34 +37,41 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
|
||||
implementation ("com.squareup.okhttp3:okhttp:4.9.0")
|
||||
implementation ("com.github.bumptech.glide:glide:4.15.1")
|
||||
kapt ("com.github.bumptech.glide:compiler:4.15.1")
|
||||
// Retrofit and OkHttp
|
||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
|
||||
implementation("com.squareup.okhttp3:okhttp:4.9.0")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
|
||||
// Glide
|
||||
implementation("com.github.bumptech.glide:glide:4.15.1")
|
||||
kapt("com.github.bumptech.glide:compiler:4.15.1")
|
||||
|
||||
// AndroidX Libraries
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("com.google.android.material:material:1.10.0")
|
||||
implementation("androidx.activity:activity:1.10.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
|
||||
|
||||
defaultLibrary()
|
||||
// Coroutines
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
|
||||
|
||||
implementation(Dependencies.AndroidX.activity)
|
||||
implementation(Dependencies.AndroidX.fragment)
|
||||
implementation(Dependencies.AndroidX.constraintLayout)
|
||||
// Hilt dependencies
|
||||
implementation("com.google.dagger:hilt-android:2.51.1")
|
||||
kapt("com.google.dagger:hilt-android-compiler:2.51.1")
|
||||
|
||||
// Navigation
|
||||
implementation(Dependencies.AndroidX.Navigation.fragment)
|
||||
implementation(Dependencies.AndroidX.Navigation.navigationUi)
|
||||
|
||||
implementation(Dependencies.Retrofit.library)
|
||||
implementation(Dependencies.Retrofit.gsonConverter)
|
||||
|
||||
|
||||
implementation("com.squareup.picasso:picasso:2.8")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
|
||||
// DataStore
|
||||
implementation("androidx.datastore:datastore-preferences:1.1.1")
|
||||
|
||||
// ML Kit
|
||||
implementation("com.google.mlkit:barcode-scanning:17.3.0")
|
||||
|
||||
// CameraX
|
||||
val cameraX = "1.3.4"
|
||||
implementation("androidx.camera:camera-core:$cameraX")
|
||||
implementation("androidx.camera:camera-camera2:$cameraX")
|
||||
@ -72,11 +79,13 @@ dependencies {
|
||||
implementation("androidx.camera:camera-view:$cameraX")
|
||||
implementation("androidx.camera:camera-mlkit-vision:1.4.0-rc04")
|
||||
|
||||
val hilt = "2.51.1"
|
||||
implementation("com.google.dagger:hilt-android:$hilt")
|
||||
kapt("com.google.dagger:hilt-android-compiler:$hilt")
|
||||
// Picasso
|
||||
implementation("com.squareup.picasso:picasso:2.8")
|
||||
|
||||
// Kotlin Serialization
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
|
||||
}
|
||||
|
||||
kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
}
|
@ -4,10 +4,9 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import ru.myitschool.work.api.ApiService
|
||||
import ru.myitschool.work.core.Constants
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@ -16,9 +15,26 @@ object ApiModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetrofit(): Retrofit {
|
||||
fun provideOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.addInterceptor(authInterceptor) // Добавляем интерсептор
|
||||
.build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAuthInterceptor(): AuthInterceptor {
|
||||
val username = "pivanov" // Замените на ваш логин
|
||||
val password = "password123" // Замените на ваш пароль
|
||||
return AuthInterceptor(username, password)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetrofit(client: OkHttpClient): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(Constants.SERVER_ADDRESS)
|
||||
.baseUrl("http://10.6.66.110:8080/") // Убедитесь, что URL корректен
|
||||
.client(client)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
}
|
||||
|
@ -1,19 +1,23 @@
|
||||
package ru.myitschool.work.api
|
||||
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.PATCH
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface ApiService {
|
||||
// Метод для аутентификации
|
||||
@POST("/api/auth") // Изменено на POST и путь к аутентификации
|
||||
@GET("/api/auth")
|
||||
suspend fun authenticate(
|
||||
@Body payload: AuthRequest // Передаем объект с логином и паролем
|
||||
): Response<UserAuthResponse> // Возвращаем UserAuthResponse
|
||||
@Query("login") login: String,
|
||||
@Query("password") password: String
|
||||
): Response<String> // Измените ResponseBody на String // Возвращаем ResponseBody вместо String
|
||||
|
||||
// Другие методы...
|
||||
@GET("/api/{login}/info") // Получение информации о пользователе
|
||||
suspend fun getUserInfo(@Path("login") login: String): Response<Map<String, Any>>
|
||||
|
||||
@ -30,17 +34,6 @@ interface ApiService {
|
||||
suspend fun getAllWorkers(): Response<List<EmployeeData>>
|
||||
}
|
||||
|
||||
// Модель данных для запроса аутентификации
|
||||
data class AuthRequest(
|
||||
val login: String,
|
||||
val password: String // Поле для пароля
|
||||
)
|
||||
|
||||
// Модель данных для ответа аутентификации
|
||||
data class UserAuthResponse(
|
||||
val role: String // Добавляем поле для роли
|
||||
)
|
||||
|
||||
// Модель данных для информации о сотруднике
|
||||
data class EmployeeData(
|
||||
val name: String,
|
||||
|
@ -1,5 +1,5 @@
|
||||
package ru.myitschool.work.core
|
||||
// БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ
|
||||
object Constants {
|
||||
const val SERVER_ADDRESS = "const val SERVER_ADDRESS = \"http://localhost:8080\"\n"
|
||||
const val SERVER_ADDRESS = "http://10.6.66.110:8080"
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package ru.myitschool.work.ui.admin
|
||||
package ru.myitschool.work.ui.Main
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.myitschool.work.ui.login
|
||||
|
||||
import ru.myitschool.work.api.ApiService
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.InputType
|
||||
@ -15,7 +16,6 @@ import androidx.navigation.fragment.findNavController
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import ru.myitschool.work.R
|
||||
import ru.myitschool.work.SessionManager
|
||||
import ru.myitschool.work.api.UserAuthResponse
|
||||
import ru.myitschool.work.databinding.FragmentLoginBinding
|
||||
import ru.myitschool.work.utils.collectWhenStarted
|
||||
import ru.myitschool.work.utils.visibleOrGone
|
||||
@ -40,7 +40,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
super.onViewCreated(view, savedInstanceState) // Убедитесь, что этот вызов находится здесь
|
||||
_binding = FragmentLoginBinding.bind(view)
|
||||
|
||||
setupUI()
|
||||
@ -85,32 +85,23 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
||||
}
|
||||
|
||||
private fun subscribe() {
|
||||
viewModel.state.collectWhenStarted(this) { state ->
|
||||
binding.loading.visibleOrGone(false)
|
||||
lifecycleScope.launch {
|
||||
viewModel.state.collect { state ->
|
||||
binding.loading.visibleOrGone(false)
|
||||
|
||||
try {
|
||||
if (state.maintenance) {
|
||||
showMaintenanceDialog() // Показываем диалог о техработах
|
||||
} else if (state.error != null) {
|
||||
binding.error.text = state.error
|
||||
binding.error.visibility = View.VISIBLE
|
||||
} else if (state.success) {
|
||||
binding.error.visibility = View.GONE
|
||||
authPreferences.saveLoginState(true)
|
||||
authPreferences.saveLogin(binding.username.text.toString()) // Сохраняем логин
|
||||
|
||||
// Сохраняем роль пользователя в SessionManager
|
||||
val userAuthResponse: UserAuthResponse? = state.userAuthResponse
|
||||
userAuthResponse?.let {
|
||||
SessionManager.userRole = it.role // Сохраняем роль
|
||||
}
|
||||
|
||||
Toast.makeText(context, "Авторизация прошла успешно", Toast.LENGTH_SHORT).show()
|
||||
navigateToMainScreen()
|
||||
navigateToMainScreen() // Перенаправление на следующий экран
|
||||
} else if (state.error != null) {
|
||||
binding.error.text = state.error
|
||||
binding.error.visibility = View.VISIBLE
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("LoginFragment", "Ошибка при обработке состояния", e)
|
||||
Toast.makeText(context, "Произошла ошибка. Пожалуйста, попробуйте снова.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,7 +110,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle("Технические работы")
|
||||
.setMessage("Проводятся техработы, пожалуйста, подождите.")
|
||||
.setPositiveButton("ОК") { dialog, _ -> dialog.dismiss() }
|
||||
.setPositiveButton("ОК ") { dialog, _ -> dialog.dismiss() }
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
|
@ -1,53 +1,41 @@
|
||||
package ru.myitschool.work.ui.login
|
||||
|
||||
import android.content.Context
|
||||
import ru.myitschool.work.api.ApiService
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Credentials
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import ru.myitschool.work.SessionManager
|
||||
import ru.myitschool.work.api.ApiService
|
||||
import ru.myitschool.work.api.AuthRequest
|
||||
import ru.myitschool.work.api.UserAuthResponse
|
||||
import ru.myitschool.work.core.Constants
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class LoginViewModel @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val apiService: ApiService
|
||||
) : ViewModel() {
|
||||
private val _state = MutableStateFlow(LoginState())
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
private val apiService: ApiService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(Constants.SERVER_ADDRESS)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
.create(ApiService::class.java)
|
||||
}
|
||||
private val _state = MutableStateFlow(LoginState())
|
||||
val state: StateFlow<LoginState> get() = _state
|
||||
|
||||
fun authenticate(username: String, password: String) {
|
||||
if (isValidUsername(username)) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val payload = AuthRequest(username, password) // Создаем объект запроса
|
||||
val response = apiService.authenticate(payload) // Вызываем метод аутентификации
|
||||
if (response.isSuccessful) {
|
||||
val userAuthResponse = response.body() // Получаем ответ с ролью пользователя
|
||||
userAuthResponse?.let {
|
||||
SessionManager.userLogin = username // Сохраняем логин
|
||||
SessionManager.userRole = it.role // Сохраняем роль
|
||||
val response = apiService.authenticate(username, password)
|
||||
Log.d("LoginViewModel", "Response code: ${response.code()}")
|
||||
|
||||
// Проверяем код ответа
|
||||
when (response.code()) {
|
||||
200 -> {
|
||||
_state.value = LoginState(success = true) // Успешная авторизация
|
||||
}
|
||||
400 -> {
|
||||
_state.value = LoginState(error = "Ошибка авторизации: Неверные учетные данные.")
|
||||
}
|
||||
else -> {
|
||||
_state.value = LoginState(error = "Ошибка авторизации: ${response.message()}")
|
||||
}
|
||||
_state.value = LoginState(success = true, userAuthResponse = userAuthResponse)
|
||||
} else {
|
||||
_state.value = LoginState(error = "Ошибка авторизации")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
@ -60,13 +48,13 @@ class LoginViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun isValidUsername(username: String): Boolean {
|
||||
return username.length >= 3 && !username.first().isDigit() && username.all { it.isLetterOrDigit() }
|
||||
return username.isNotEmpty() // Пример проверки логина
|
||||
}
|
||||
}
|
||||
|
||||
data class LoginState(
|
||||
val success: Boolean = false,
|
||||
val error: String? = null,
|
||||
val maintenance: Boolean = false,
|
||||
val userAuthResponse: UserAuthResponse? = null // Добавляем поле для хранения информации о роли
|
||||
)
|
||||
}
|
||||
// Состояние аутентификации
|
||||
data class LoginState(
|
||||
val success: Boolean = false, // Успешность аутентификации
|
||||
val error: String? = null, // Сообщение об ошибке
|
||||
val maintenance: Boolean = false // Состояние техобслуживания
|
||||
)
|
@ -11,10 +11,10 @@
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Введите логин"
|
||||
android:hint="@string/username_hint"
|
||||
android:inputType="text"
|
||||
android:padding="12dp"
|
||||
android:background="#F0F0F0"
|
||||
android:background="@drawable/ic_android_black_24dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<EditText
|
||||
@ -24,17 +24,18 @@
|
||||
android:hint="Введите пароль"
|
||||
android:inputType="textPassword"
|
||||
android:padding="12dp"
|
||||
android:background="#F0F0F0"
|
||||
android:background="@drawable/ic_android_black_24dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Войти"
|
||||
android:text="@string/login_button"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:textColor="@android:color/white"
|
||||
android:padding="12dp"
|
||||
app:cornerRadius="16dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<TextView
|
||||
|
@ -12,7 +12,7 @@
|
||||
android:id="@+id/fullname"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Имя Фамилия"
|
||||
android:text="@string/fullname_label"
|
||||
android:textSize="18sp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:visibility="gone" />
|
||||
@ -32,7 +32,7 @@
|
||||
android:id="@+id/position"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Должность"
|
||||
android:text="@string/position_label"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
@ -71,7 +71,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="visible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- Кнопки -->
|
||||
<Button
|
||||
@ -98,7 +98,8 @@
|
||||
android:id="@+id/admin_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Admin Panel"
|
||||
android:text="@string/admin_panel"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
android:contentDescription="@string/close_button"
|
||||
android:src="@drawable/ic_close"
|
||||
app:elevation="0dp"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -11,7 +11,7 @@
|
||||
android:id="@+id/result"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Результат"
|
||||
android:text="@string/result"
|
||||
android:textSize="18sp"
|
||||
android:gravity="center"
|
||||
android:padding="16dp" />
|
||||
@ -20,7 +20,9 @@
|
||||
android:id="@+id/close"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Закрыть"
|
||||
android:text="@string/close_button"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:textColor="@android:color/white"
|
||||
android:padding="12dp"
|
||||
android:layout_marginTop="24dp" />
|
||||
|
||||
|
@ -32,4 +32,6 @@
|
||||
<string name="qr_scan_success">Successfully</string>
|
||||
<string name="qr_scan_failure">Something went wrong.</string>
|
||||
<string name="qr_scan_cancelled">Operation was cancelled</string>
|
||||
<string name="close_button">Close</string>
|
||||
<string name="result">Result</string>
|
||||
</resources>
|
@ -32,8 +32,7 @@
|
||||
<string name="qr_scan_success">Успешно</string>
|
||||
<string name="qr_scan_failure">Что-то пошло не так</string>
|
||||
<string name="qr_scan_cancelled">Вход был отменён / Operation was cancelled</string>
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||
|
||||
|
||||
<string name="close_button">Закрыть</string>
|
||||
<string name="result">Результат</string>
|
||||
<string name="admin_panel" translatable="false">Admin Panel</string>
|
||||
</resources>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="close_button" translatable="false">Close</string>
|
||||
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user