Merge branch 'Frontend' into Frontend_UI
# Conflicts: # app/src/main/AndroidManifest.xml # app/src/main/java/ru/myitschool/work/ui/Main/AdminFragment.kt # app/src/main/res/layout/fragment_main.xml
This commit is contained in:
commit
ee24f241db
@ -47,6 +47,10 @@ dependencies {
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
kapt ("com.github.bumptech.glide:compiler:4.15.1")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
|
||||
|
||||
defaultLibrary()
|
||||
|
||||
implementation(Dependencies.AndroidX.activity)
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"> <!-- Минимальная и целевая версия SDK по тз (28 и 34) -->
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- Минимальная и целевая версия SDK по тз (28 и 34) -->
|
||||
<uses-sdk
|
||||
android:minSdkVersion="28"
|
||||
android:targetSdkVersion="34" />
|
||||
@ -22,9 +24,6 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Default"
|
||||
tools:targetApi="34">
|
||||
<activity
|
||||
android:name=".ui.Main.AdminFragment"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.RootActivity"
|
||||
android:exported="true">
|
||||
|
@ -1,5 +1,12 @@
|
||||
package ru.myitschool.work
|
||||
|
||||
object SessionManager {
|
||||
var userLogin: String = ""
|
||||
var userLogin: String? = null // Логин пользователя
|
||||
var userRole: String? = null // Роль пользователя
|
||||
|
||||
// Метод для очистки данных сессии
|
||||
fun clearSession() {
|
||||
userLogin = null
|
||||
userRole = null
|
||||
}
|
||||
}
|
@ -4,9 +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
|
||||
|
||||
@ -16,9 +16,20 @@ object ApiModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetrofit(): Retrofit {
|
||||
fun provideOkHttpClient(): OkHttpClient {
|
||||
val username = "pivanov" // Замените на ваш логин
|
||||
val password = "password123" // Замените на ваш пароль
|
||||
return OkHttpClient.Builder()
|
||||
.addInterceptor(AuthInterceptor(username, password))
|
||||
.build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetrofit(client: OkHttpClient): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(Constants.SERVER_ADDRESS)
|
||||
.client(client)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
}
|
||||
|
@ -3,22 +3,56 @@ package ru.myitschool.work.api
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.PATCH
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface ApiService {
|
||||
@GET("api/{login}/auth")
|
||||
// Метод для аутентификации
|
||||
@GET("/api/auth") // Используем GET для аутентификации
|
||||
suspend fun authenticate(
|
||||
@Path("login") login: String,
|
||||
@Header("Authorization") authorization: String // Добавляем заголовок Authorization
|
||||
): Response<Unit>
|
||||
@Query("login") login: String, // Передаем логин как параметр запроса
|
||||
@Query("password") password: String // Передаем пароль как параметр запроса
|
||||
): Response<UserAuthResponse> // Возвращаем JSON как объект UserAuthResponse
|
||||
|
||||
@GET("api/{login}/info")
|
||||
suspend fun getUserInfo(@Path("login") login: String): Response<Map<String, Any>> // Возвращаем Map вместо UserInfo
|
||||
// Другие методы...
|
||||
@GET("/api/{login}/info") // Получение информации о пользователе
|
||||
suspend fun getUserInfo(@Path("login") login: String): Response<Map<String, Any>>
|
||||
|
||||
@PATCH("api/{login}/open")
|
||||
suspend fun openDoor(@Path("login") login: String, @Body body: OpenDoorRequest): Response<Unit>
|
||||
@GET("/api/employee/{login}") // Получение информации о сотруднике
|
||||
suspend fun getEmployeeInfo(@Path("login") login: String): Response<EmployeeData>
|
||||
|
||||
@PATCH("/api/open") // Открыть дверь
|
||||
suspend fun openDoor(@Body request: OpenDoorRequest): Response<String>
|
||||
|
||||
@POST("/api/employee/toggleAccess") // Метод для блокировки/разблокировки доступа
|
||||
suspend fun toggleAccess(@Body request: ToggleAccessRequest): Response<Unit>
|
||||
|
||||
@GET("/api/workers") // Получить всех сотрудников
|
||||
suspend fun getAllWorkers(): Response<List<EmployeeData>>
|
||||
}
|
||||
|
||||
data class OpenDoorRequest(val value: String)
|
||||
// Модель данных для ответа аутентификации
|
||||
data class UserAuthResponse(
|
||||
val role: String // Добавляем поле для роли
|
||||
)
|
||||
|
||||
// Модель данных для информации о сотруднике
|
||||
data class EmployeeData(
|
||||
val name: String,
|
||||
val position: String,
|
||||
val lastVisit: String
|
||||
)
|
||||
|
||||
// Модель данных для запроса блокировки/разблокировки доступа
|
||||
data class ToggleAccessRequest(
|
||||
val login: String, // Логин сотрудника
|
||||
val action: String // Действие: "block" или "unblock"
|
||||
)
|
||||
|
||||
// Модель данных для запроса открытия двери
|
||||
data class OpenDoorRequest(
|
||||
val login: String, // Логин сотрудника
|
||||
val value: Long // Код для открытия двери
|
||||
)
|
16
app/src/main/java/ru/myitschool/work/api/AuthInterceptor.kt
Normal file
16
app/src/main/java/ru/myitschool/work/api/AuthInterceptor.kt
Normal file
@ -0,0 +1,16 @@
|
||||
package ru.myitschool.work.api
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import java.util.Base64
|
||||
|
||||
class AuthInterceptor(private val username: String, private val password: String) : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val originalRequest = chain.request()
|
||||
val credential = Base64.getEncoder().encodeToString("$username:$password".toByteArray())
|
||||
val newRequest = originalRequest.newBuilder()
|
||||
.addHeader("Authorization", "Basic $credential")
|
||||
.build()
|
||||
return chain.proceed(newRequest)
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
package ru.myitschool.work.core
|
||||
// БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ
|
||||
object Constants {
|
||||
const val SERVER_ADDRESS = "const val SERVER_ADDRESS = \"http://localhost:8090\"\n"
|
||||
const val SERVER_ADDRESS = "http://192.168.1.131:8080"
|
||||
}
|
@ -1,21 +1,108 @@
|
||||
package ru.myitschool.work.ui.Main
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.launch
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import ru.myitschool.work.R
|
||||
import ru.myitschool.work.api.ApiService
|
||||
import ru.myitschool.work.api.EmployeeData
|
||||
import ru.myitschool.work.api.ToggleAccessRequest
|
||||
import ru.myitschool.work.core.Constants
|
||||
import ru.myitschool.work.databinding.FragmentAdminBinding
|
||||
|
||||
class AdminFragment : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.fragment_admin)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
class AdminFragment : Fragment(R.layout.fragment_admin) {
|
||||
private var _binding: FragmentAdminBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val apiService: ApiService by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(Constants.SERVER_ADDRESS)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
.create(ApiService::class.java)
|
||||
}
|
||||
|
||||
private var isAccessBlocked: Boolean = false // Переменная для отслеживания состояния доступа
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
_binding = FragmentAdminBinding.bind(view)
|
||||
|
||||
setupUI()
|
||||
}
|
||||
|
||||
private fun setupUI() {
|
||||
binding.viewEmployeeInfo.setOnClickListener {
|
||||
val login = binding.employeeLogin.text.toString()
|
||||
if (login.isNotEmpty()) {
|
||||
fetchEmployeeInfo(login)
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Введите логин сотрудника", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
binding.toggleAccess.setOnClickListener {
|
||||
val login = binding.employeeLogin.text.toString()
|
||||
if (login.isNotEmpty()) {
|
||||
// Определяем действие на основе текущего состояния доступа
|
||||
val action = if (isAccessBlocked) "unblock" else "block"
|
||||
toggleEmployeeAccess(login, action)
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Введите логин сотрудника", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchEmployeeInfo(login: String) {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val response = apiService.getEmployeeInfo(login)
|
||||
if (response.isSuccessful) {
|
||||
val employeeData = response.body()
|
||||
employeeData?.let {
|
||||
binding.employeeInfo.text = "Имя: ${it.name}, Должность: ${it.position}, Последний визит: ${it.lastVisit}"
|
||||
binding.employeeInfo.visibility = View.VISIBLE
|
||||
binding.toggleAccess.visibility = View.VISIBLE
|
||||
// Здесь можно установить состояние доступа, если оно доступно
|
||||
isAccessBlocked = false // Предположим, что доступ не заблокирован по умолчанию
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Ошибка получения данных", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Toast.makeText(requireContext(), "Ошибка сети", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleEmployeeAccess(login: String, action: String) {
|
||||
val request = ToggleAccessRequest(login, action)
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val response = apiService.toggleAccess(request)
|
||||
if (response.isSuccessful) {
|
||||
isAccessBlocked = !isAccessBlocked // Переключаем состояние доступа
|
||||
val message = if (action == "block") "Доступ заблокирован" else "Доступ разблокирован"
|
||||
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Ошибка изменения доступа", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Toast.makeText(requireContext(), "Ошибка сети", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
_binding = null // Освобождаем binding, когда представление уничтожается
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ import androidx.fragment.app.setFragmentResultListener
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -47,11 +46,24 @@ class MainFragment : Fragment(R.layout.fragment_main) {
|
||||
|
||||
setupUI()
|
||||
fetchUserData()
|
||||
checkAdminAccess() // Проверяем доступ администратора
|
||||
|
||||
// Проверяем, есть ли результат QR
|
||||
checkQrResult()
|
||||
}
|
||||
|
||||
private fun checkAdminAccess() {
|
||||
// Проверяем, является ли пользователь администратором
|
||||
if (SessionManager.userRole == "admin") {
|
||||
binding.adminPanel.visibility = View.VISIBLE // Показываем кнопку AdminPanel
|
||||
binding.adminPanel.setOnClickListener {
|
||||
findNavController().navigate(R.id.adminFragment) // Переход на экран администратора
|
||||
}
|
||||
} else {
|
||||
binding.adminPanel.visibility = View.GONE // Скрываем кнопку для обычных пользователей
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkQrResult() {
|
||||
// Слушаем результат QR сканирования
|
||||
setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle ->
|
||||
@ -81,25 +93,28 @@ class MainFragment : Fragment(R.layout.fragment_main) {
|
||||
lifecycleScope.launch {
|
||||
showError(null) // Скрыть ошибку, если она была
|
||||
try {
|
||||
val response = apiService.getUserInfo(SessionManager.userLogin) // Получаем данные пользователя
|
||||
if (response.isSuccessful) {
|
||||
response.body()?.let { data ->
|
||||
// Извлекаем значения из Map
|
||||
val fullName = data["name"] as? String ?: "Неизвестно"
|
||||
val position = data["position"] as? String ?: "Неизвестно"
|
||||
val lastVisit = data["lastVisit"] as? String ?: "Неизвестно"
|
||||
val photoUrl = data["photo"] as? String ?: ""
|
||||
val response =
|
||||
SessionManager.userLogin?.let { apiService.getUserInfo(it) } // Получаем данные пользователя
|
||||
if (response != null) {
|
||||
if (response.isSuccessful) {
|
||||
response.body()?.let { data ->
|
||||
// Извлекаем значения из Map
|
||||
val fullName = data["name"] as? String ?: "Неизвестно"
|
||||
val position = data["position"] as? String ?: "Неизвестно"
|
||||
val lastVisit = data["lastVisit"] as? String ?: "Неизвестно"
|
||||
val photoUrl = data["photo"] as? String ?: ""
|
||||
|
||||
// Обновляем UI
|
||||
updateUI(fullName, position, lastVisit, photoUrl)
|
||||
// Обновляем UI
|
||||
updateUI(fullName, position, lastVisit, photoUrl)
|
||||
|
||||
// Здесь вы можете добавить данные проходов в список
|
||||
// Пример:
|
||||
accessLogs.add(AccessLog("2024-02-31 08:31", "Считыватель 1", "карта"))
|
||||
accessLogAdapter.notifyDataSetChanged() // Обновляем адаптер
|
||||
// Здесь вы можете добавить данные проходов в список
|
||||
// Пример:
|
||||
accessLogs.add(AccessLog("2024-02-31 08:31", "Считыватель 1", "карта"))
|
||||
accessLogAdapter.notifyDataSetChanged() // Обновляем адаптер
|
||||
}
|
||||
} else {
|
||||
showError(getString(R.string.error_loading_data)) // Показываем ошибку, если данные не загрузились
|
||||
}
|
||||
} else {
|
||||
showError(getString(R.string.error_loading_data)) // Показываем ошибку, если данные не загрузились
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showError(e.localizedMessage) // Показываем ошибку при исключении
|
||||
|
@ -10,15 +10,17 @@ import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
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
|
||||
import ru.myitschool.work.utils.AuthPreferences
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
||||
@AndroidEntryPoint
|
||||
class LoginFragment : Fragment(R.layout.fragment_login) {
|
||||
@ -57,6 +59,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
||||
performLogin(username, password) // Передаем пароль в метод performLogin
|
||||
}
|
||||
|
||||
// Изначально скрываем индикаторы
|
||||
binding.loading.visibleOrGone(false)
|
||||
binding.error.visibleOrGone(false)
|
||||
}
|
||||
@ -76,30 +79,35 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
||||
|
||||
private fun performLogin(username: String, password: String) {
|
||||
lifecycleScope.launch {
|
||||
binding.loading.visibleOrGone(true) // Показываем индикатор загрузки
|
||||
viewModel.authenticate(username, password) // Передаем пароль в метод authenticate
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,7 +116,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,52 +1,48 @@
|
||||
package ru.myitschool.work.ui.login
|
||||
|
||||
import android.content.Context
|
||||
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.core.Constants
|
||||
import ru.myitschool.work.api.UserAuthResponse
|
||||
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 credentials = Credentials.basic(username, password) // Создаем Basic Auth заголовок
|
||||
val response = apiService.authenticate(username, credentials) // Передаем заголовок в запрос
|
||||
val response = apiService.authenticate(username, password)
|
||||
Log.d("LoginViewModel", "Response code: ${response.code()}")
|
||||
|
||||
if (response.isSuccessful) {
|
||||
SessionManager.userLogin = username
|
||||
_state.value = LoginState(success = true)
|
||||
} else if (response.code() == 503) { // Пример кода для техработ
|
||||
_state.value = LoginState(maintenance = true)
|
||||
val userAuthResponse = response.body() // Получаем JSON-ответ
|
||||
Log.d("LoginViewModel", "User Auth Response: $userAuthResponse") // Логируем ответ
|
||||
|
||||
// Обработка JSON-ответа
|
||||
if (userAuthResponse != null) {
|
||||
SessionManager.userLogin = username
|
||||
SessionManager.userRole = userAuthResponse.role // Сохраняем роль
|
||||
_state.value = LoginState(success = true) // Успешная авторизация
|
||||
} else {
|
||||
_state.value = LoginState(error = "Ошибка авторизации: Неверные учетные данные.")
|
||||
}
|
||||
} else {
|
||||
_state.value = LoginState(error = "Ошибка авторизации")
|
||||
_state.value = LoginState(error = "Ошибка авторизации: ${response.message()}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Логирование ошибки
|
||||
e.printStackTrace()
|
||||
_state.value = LoginState(error = "Ошибка сети. Проверьте подключение к интернету.")
|
||||
}
|
||||
@ -57,8 +53,14 @@ 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)
|
||||
}
|
||||
// Состояние аутентификации
|
||||
data class LoginState(
|
||||
val success: Boolean = false, // Успешность аутентификации
|
||||
val userAuthResponse: UserAuthResponse? = null, // Ответ с информацией о пользователе
|
||||
val error: String? = null, // Сообщение об ошибке
|
||||
val maintenance: Boolean = false // Состояние техобслуживания
|
||||
)
|
@ -55,11 +55,19 @@ class QrResult : Fragment(R.layout.fragment_qr_scan_result) {
|
||||
private fun sendRequestToServer(qrData: String) {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val response = apiService.openDoor(SessionManager.userLogin, OpenDoorRequest(qrData))
|
||||
if (response.isSuccessful) {
|
||||
binding.result.text = "Успешно/Success"
|
||||
// Проверяем, что userLogin не равен null
|
||||
val login = SessionManager.userLogin
|
||||
if (login != null) {
|
||||
// Создаем объект OpenDoorRequest с логином и кодом
|
||||
val openDoorRequest = OpenDoorRequest(login, qrData.toLong()) // Преобразуем qrData в Long, если это необходимо
|
||||
val response = apiService.openDoor(openDoorRequest) // Теперь передаем только openDoorRequest
|
||||
if (response.isSuccessful) {
|
||||
binding.result.text = "Успешно/Success"
|
||||
} else {
|
||||
binding.result.text = "Что-то пошло не так/Something wrong"
|
||||
}
|
||||
} else {
|
||||
binding.result.text = "Что-то пошло не так/Something wrong"
|
||||
binding.result.text = "Пользователь не авторизован/Unauthorized user"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
binding.result.text = "Что-то пошло не так/Something wrong"
|
||||
|
24
app/src/main/java/utils/Extensions.kt
Normal file
24
app/src/main/java/utils/Extensions.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package ru.myitschool.work.utils
|
||||
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
// Функция для сбора данных из Flow, когда жизненный цикл находится в состоянии STARTED
|
||||
fun <T> Flow<T>.collectWhenStarted(lifecycleOwner: LifecycleOwner, collector: (T) -> Unit) {
|
||||
lifecycleOwner.lifecycleScope.launch {
|
||||
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
collect { value -> collector(value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для управления видимостью View
|
||||
fun View?.visibleOrGone(isVisible: Boolean) {
|
||||
this?.visibility = if (isVisible) View.VISIBLE else View.GONE
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
android:text="@string/fullname_label"
|
||||
android:textSize="18sp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:visibility="visible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- Фото пользователя. -->
|
||||
<ImageView
|
||||
@ -25,7 +25,7 @@
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/photo_description"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:visibility="visible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- Поле для должности -->
|
||||
<TextView
|
||||
@ -34,7 +34,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/position_label"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:visibility="visible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- Поле для даты последнего входа -->
|
||||
<TextView
|
||||
@ -43,7 +43,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="2024-02-31 08:31"
|
||||
android:layout_marginBottom="75dp"
|
||||
android:visibility="visible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- Кнопка обновления -->
|
||||
<Button
|
||||
@ -63,7 +63,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/error_placeholder"
|
||||
android:textColor="@android:color/holo_red_dark"
|
||||
android:visibility="visible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<!-- RecyclerView для списка проходов -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
@ -82,7 +82,7 @@
|
||||
android:layout_marginBottom="12dp"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:textColor="@android:color/white"
|
||||
android:visibility="visible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/logout"
|
||||
@ -92,5 +92,14 @@
|
||||
android:layout_marginBottom="50dp"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:textColor="@android:color/white"
|
||||
android:visibility="visible" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/admin_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Admin Panel"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -22,6 +22,12 @@
|
||||
android:label="Main Fragment"
|
||||
tools:layout="@layout/fragment_main" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/adminFragment"
|
||||
android:name="ru.myitschool.work.ui.admin.AdminFragment"
|
||||
android:label="Admin Fragment"
|
||||
tools:layout="@layout/fragment_admin" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/qrResultFragment"
|
||||
android:name="ru.myitschool.work.ui.qr.result.QrResult"
|
||||
|
Loading…
x
Reference in New Issue
Block a user