From 1efdfa501a09d7e31d9f46f759976d88b09d2358 Mon Sep 17 00:00:00 2001 From: Universall Date: Thu, 20 Feb 2025 11:04:53 +0300 Subject: [PATCH 1/3] few changes --- app/src/main/res/values-ru/strings-ru.xml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/values-ru/strings-ru.xml b/app/src/main/res/values-ru/strings-ru.xml index 291f41e..a2fcece 100644 --- a/app/src/main/res/values-ru/strings-ru.xml +++ b/app/src/main/res/values-ru/strings-ru.xml @@ -1,6 +1,6 @@ - AS + AS Введите логин Введите пароль Здравствуйте! @@ -15,14 +15,13 @@ Логина не существует или неверный ОК Ошибка - Введите логин пользователя - Поиск - Профиль - Изменить права входа с помощью смартфона - >Панель администратораProfile - Log in to your account - QR code scanning - Scan result - Checking the session employee - + Введите логин пользователя + Поиск + Профиль + Изменить права входа с помощью смартфона + Панель администратора + Log in to your account + QR code scanning + Scan result + Checking the session employee \ No newline at end of file From 277db36d797b46f4423645d907c5424b1e617677 Mon Sep 17 00:00:00 2001 From: Universall Date: Thu, 20 Feb 2025 11:41:48 +0300 Subject: [PATCH 2/3] Refactor --- .../auth/internal_utils/UserManager.java | 35 ++++---- .../com/displaynone/acss/config/Constants.kt | 2 +- .../acss/ui/admin/AdminFragment.kt | 2 +- .../displaynone/acss/ui/init/InitFragment.kt | 9 +- .../acss/ui/profile/ProfileFragment.kt | 86 +++++++++---------- .../acss/ui/result/QrResultFragment.kt | 39 ++++----- .../acss/ui/scan/QrScanFragment.kt | 8 +- .../acss/ui/scan/QrScanViewModel.kt | 3 +- app/src/main/res/navigation/nav_graph.xml | 2 +- 9 files changed, 89 insertions(+), 97 deletions(-) diff --git a/app/src/main/java/com/displaynone/acss/components/auth/internal_utils/UserManager.java b/app/src/main/java/com/displaynone/acss/components/auth/internal_utils/UserManager.java index 2c9397c..24cdfa1 100644 --- a/app/src/main/java/com/displaynone/acss/components/auth/internal_utils/UserManager.java +++ b/app/src/main/java/com/displaynone/acss/components/auth/internal_utils/UserManager.java @@ -7,21 +7,25 @@ import androidx.annotation.Nullable; import androidx.security.crypto.EncryptedSharedPreferences; import androidx.security.crypto.MasterKeys; -import com.displaynone.acss.components.auth.models.AuthTokenPair; import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO; import com.google.gson.Gson; import java.io.IOException; import java.security.GeneralSecurityException; -import java.util.Collections; -import java.util.Optional; public class UserManager { + // Preferences private static final String _PREFERENCES_FILENAME = "userData"; private final SharedPreferences _preferences; + + // Utils + private static final Gson gson = new Gson(); + + // Keys + private static final String _KEY_USER = "user"; + + // Cache private UserDTO userDTO; - private static final String ACCESS_KEY = "user"; - private Gson gson = new Gson(); public UserManager(Context context) { this._preferences = this._createEncryptedPreferences(context); @@ -40,23 +44,24 @@ public class UserManager { throw new RuntimeException(e); } } + public void saveDto(UserDTO userDTO) { this.userDTO = userDTO; _preferences.edit() - .putString(ACCESS_KEY,toJson(userDTO)) + .putString(_KEY_USER, toJson(userDTO)) .apply(); } + public @Nullable UserDTO getDto() { if (this.userDTO != null) return this.userDTO; - UserDTO userDTO = fromJson( _preferences.getString(ACCESS_KEY, null)); - return userDTO; - } - private UserDTO fromJson(String userJSON){ - UserDTO userDTO = gson.fromJson(userJSON, UserDTO.class); - return userDTO; - } - private String toJson(UserDTO userDTO){ - return gson.toJson(userDTO); + return fromJson(_preferences.getString(_KEY_USER, null)); } + private UserDTO fromJson(String userJSON) { + return gson.fromJson(userJSON, UserDTO.class); + } + + private String toJson(UserDTO userDTO) { + return gson.toJson(userDTO); + } } diff --git a/app/src/main/java/com/displaynone/acss/config/Constants.kt b/app/src/main/java/com/displaynone/acss/config/Constants.kt index 5fb94b1..350a0dc 100644 --- a/app/src/main/java/com/displaynone/acss/config/Constants.kt +++ b/app/src/main/java/com/displaynone/acss/config/Constants.kt @@ -1,5 +1,5 @@ package com.displaynone.acss.config object Constants { - const val serverUrl = "http://192.168.136.38:8086" + const val serverUrl = "http://192.168.56.1:8086" } diff --git a/app/src/main/java/com/displaynone/acss/ui/admin/AdminFragment.kt b/app/src/main/java/com/displaynone/acss/ui/admin/AdminFragment.kt index 8b4db77..b910c1e 100644 --- a/app/src/main/java/com/displaynone/acss/ui/admin/AdminFragment.kt +++ b/app/src/main/java/com/displaynone/acss/ui/admin/AdminFragment.kt @@ -46,7 +46,7 @@ class AdminFragment : Fragment(R.layout.fragment_admin) { } if (state is AdminViewModel.State.Error){ val errorMessage = state.errorMessage - binding.loginSearch.setError(errorMessage) + binding.loginSearch.error = errorMessage } } } diff --git a/app/src/main/java/com/displaynone/acss/ui/init/InitFragment.kt b/app/src/main/java/com/displaynone/acss/ui/init/InitFragment.kt index a08fedd..567c01d 100644 --- a/app/src/main/java/com/displaynone/acss/ui/init/InitFragment.kt +++ b/app/src/main/java/com/displaynone/acss/ui/init/InitFragment.kt @@ -80,10 +80,6 @@ class InitFragment : Fragment(R.layout.fragment_init) { return false } - private fun getCachedUser(): UserDTO? { - return UserServiceST.getInstance().getUserDTO(); - } - private fun isUserAuthenticated(): Boolean { return UserServiceST.getInstance().hasTokens() } @@ -91,4 +87,9 @@ class InitFragment : Fragment(R.layout.fragment_init) { private fun handleError(string: Int) { binding.error.text = requireContext().getString(string) } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } } \ No newline at end of file diff --git a/app/src/main/java/com/displaynone/acss/ui/profile/ProfileFragment.kt b/app/src/main/java/com/displaynone/acss/ui/profile/ProfileFragment.kt index 57fee69..8dc9166 100644 --- a/app/src/main/java/com/displaynone/acss/ui/profile/ProfileFragment.kt +++ b/app/src/main/java/com/displaynone/acss/ui/profile/ProfileFragment.kt @@ -11,8 +11,8 @@ import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import com.bumptech.glide.Glide import com.displaynone.acss.R -import com.displaynone.acss.components.auth.models.user.UserServiceST import com.displaynone.acss.components.acs.models.visit.VisitAdapter +import com.displaynone.acss.components.auth.models.user.UserServiceST import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO import com.displaynone.acss.databinding.FragmentProfileBinding import com.displaynone.acss.ui.profile.ProfileViewModel.Action @@ -20,7 +20,7 @@ import com.displaynone.acss.ui.scan.QrScanDestination import com.displaynone.acss.util.collectWithLifecycle import com.displaynone.acss.util.navigateTo -class ProfileFragment: Fragment(R.layout.fragment_profile) { +class ProfileFragment : Fragment(R.layout.fragment_profile) { private var _binding: FragmentProfileBinding? = null private val binding: FragmentProfileBinding get() = _binding!! @@ -33,29 +33,28 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) { checkForAdmin() binding.swipeRefresh.setOnRefreshListener { - if (getIsMe()){ - refreshData() - } else{ - showData(getUserDto()!!) - } + if (getIsMe()) refreshData() else showData(getUserDto()!!); } - binding.logout.setOnClickListener{ + + binding.logout.setOnClickListener { logout() } - binding.scan.setOnClickListener{ + + binding.scan.setOnClickListener { navigateTo(view, R.id.action_profileFragment_to_qrScanFragment) } - binding.buttonSearch.setOnClickListener{ + + binding.buttonSearch.setOnClickListener { navigateTo(view, R.id.action_profileFragment_to_adminFragment) } + binding.recyclerViewLogs.adapter = adapter + if (getIsMe()) { refreshData() - viewModel.visitListState.collectWithLifecycle(this) { data -> - adapter.submitData(data) - } + viewModel.visitListState.collectWithLifecycle(this) { data -> adapter.submitData(data) } waitForQRScanResult() - } else{ + } else { showData(getUserDto()!!) Log.d("ProfileFragment", "set login") viewModel.visitListStateFromLogin.collectWithLifecycle(this) { data -> @@ -66,72 +65,66 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) { } subscribe() binding.recyclerViewLogs.layoutManager = LinearLayoutManager(requireContext()) -// viewModel.visitListStateFromLogin.collectWithLifecycle(this) { data -> -// adapter.submitData(data) -// } - } + private fun checkForAdmin() { - Log.d("check", "cheking for roles") + val userDTO = UserServiceST.getInstance().getUserDTO() ?: return - val userDTO = UserServiceST.getInstance().getUserDTO() - if (userDTO != null) { - if (userDTO.roles.any { it.name == "ROLE_ADMIN" }) { - Log.d("adminlog", "i'm admin") - binding.buttonSearch.visibility = View.VISIBLE - binding.rightsUsingSmartphone.text = "Пропуск действителен" - } - if (userDTO.roles.any { it.name == "ROLE_USER" }) { - Log.d("userlog", "i'm user") - - binding.rightsUsingSmartphone.text = "Пропуск действителен" - } + if (userDTO.roles.any { it.name == "ROLE_ADMIN" }) { + binding.buttonSearch.visibility = View.VISIBLE + binding.rightsUsingSmartphone.text = "Пропуск действителен" + } + if (userDTO.roles.any { it.name == "ROLE_USER" }) { + binding.rightsUsingSmartphone.text = "Пропуск действителен" } } + private fun hideButtons() { binding.logout.visibility = View.GONE binding.scan.visibility = View.GONE binding.buttonSearch.visibility = View.GONE } - fun showMyData(userDTO: UserDTO){ + + private fun showMyData(userDTO: UserDTO) { binding.fio.text = userDTO.name binding.position.text = userDTO.position setAvatar(userDTO.photo) } - fun showData(userDTO: UserDTO){ + + private fun showData(userDTO: UserDTO) { binding.fio.text = userDTO.name binding.position.text = userDTO.position viewModel.setLogin(login1 = userDTO.login) - Log.d("ProfileFragment", userDTO.login) - -// binding.lastEntry.text = userDTO.lastVisit - setAvatar(userDTO.photo) } + private fun refreshData() { - Log.d("ProfileFragment", "Refreshed") viewModel.getInfo() subscribeToGetData() } - fun subscribe() { + + private fun subscribe() { viewModel.action.collectWithLifecycle(this) { action -> if (action is Action.GoToAuth) { - view?.let { navigateTo(it, R.id.action_profileFragment_to_authFragment) } ?: throw IllegalStateException("View is null") + view?.let { navigateTo(it, R.id.action_profileFragment_to_authFragment) } + ?: throw IllegalStateException("View is null") } if (action is Action.GoToScan) { - view?.let { navigateTo(it, R.id.action_profileFragment_to_qrResultFragment) }?: throw IllegalStateException("View is null") + view?.let { navigateTo(it, R.id.action_profileFragment_to_qrResultFragment) } + ?: throw IllegalStateException("View is null") } } } + private fun getUserDto(): UserDTO? { return arguments?.getSerializable("user") as? UserDTO } + private fun getIsMe(): Boolean { return arguments?.getBoolean("isMe", true) ?: true } private fun waitForQRScanResult() { - requireActivity().onBackPressedDispatcher.addCallback( viewLifecycleOwner, object : OnBackPressedCallback(true) { @@ -145,20 +138,19 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) { )?.observe(viewLifecycleOwner) { bundle -> val qrCode = bundle.getString("key_qr") if (!qrCode.isNullOrEmpty()) view?.let { - val bundle = Bundle().apply { - putString("qrCode", qrCode) - } - navigateTo(it, R.id.action_profileFragment_to_qrResultFragment, bundle) + val newBundle = Bundle().apply { putString("qrCode", qrCode) } + navigateTo(it, R.id.action_profileFragment_to_qrResultFragment, newBundle) } } } + private fun logout() { viewModel.logout() viewModel.openAuth() Toast.makeText(activity, "LOGOUT", Toast.LENGTH_SHORT).show() } - private fun subscribeToGetData(){ + private fun subscribeToGetData() { viewModel.state.collectWithLifecycle(this) { state -> if (state is ProfileViewModel.State.Show) { val userDto: UserDTO = state.item diff --git a/app/src/main/java/com/displaynone/acss/ui/result/QrResultFragment.kt b/app/src/main/java/com/displaynone/acss/ui/result/QrResultFragment.kt index 4f47f45..34de52b 100644 --- a/app/src/main/java/com/displaynone/acss/ui/result/QrResultFragment.kt +++ b/app/src/main/java/com/displaynone/acss/ui/result/QrResultFragment.kt @@ -1,13 +1,9 @@ package com.displaynone.acss.ui.result -import android.content.Context -import android.content.SharedPreferences import android.os.Bundle -import android.util.Log import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import androidx.navigation.fragment.findNavController import com.displaynone.acss.R import com.displaynone.acss.databinding.FragmentQrResultBinding import com.displaynone.acss.util.collectWithLifecycle @@ -21,34 +17,37 @@ class QrResultFragment : Fragment(R.layout.fragment_qr_result) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - Log.d("QrResultFragment", getQrCode()) _binding = FragmentQrResultBinding.bind(view) binding.close.setOnClickListener(this::closeQrScanFragment) this.openDoor() - viewModel.state.collectWithLifecycle(this){ state -> - if (state is QrResultViewModel.State.Result){ - if (state.resultCode == 200) { - setResult(getString(R.string.success)) - } else if (state.resultCode == 400) { - setResult(getString(R.string.wrong)) - } else if (state.resultCode == 401) { - setResult(getString(R.string.cancel)) + viewModel.state.collectWithLifecycle(this) { state -> + if (state is QrResultViewModel.State.Result) { + when (state.resultCode) { + 200 -> { + setResult(getString(R.string.success)) + } + + 400 -> { + setResult(getString(R.string.wrong)) + } + + 401 -> { + setResult(getString(R.string.cancel)) + } } } - if (state is QrResultViewModel.State.Error){ - setResult(state.errorMessage) - } + if (state is QrResultViewModel.State.Error) setResult(state.errorMessage) } } private fun openDoor() { + val qrValue = getQrCode() ?: return val qrCodeValueLong: Long - try { - qrCodeValueLong = getQrCode().toLong() + qrCodeValueLong = qrValue.toLong() } catch (exception: Exception) { when (exception) { is NumberFormatException, is IllegalArgumentException -> setResult(getString(R.string.wrong)) @@ -60,8 +59,8 @@ class QrResultFragment : Fragment(R.layout.fragment_qr_result) { viewModel.openDoor(qrCodeValueLong) } - private fun getQrCode(): String { - return arguments?.getString("qrCode") ?: "No QR Code Provided" + private fun getQrCode(): String? { + return arguments?.getString("qrCode") } private fun closeQrScanFragment(view: View) { diff --git a/app/src/main/java/com/displaynone/acss/ui/scan/QrScanFragment.kt b/app/src/main/java/com/displaynone/acss/ui/scan/QrScanFragment.kt index b1f3c2a..ca6d1d0 100644 --- a/app/src/main/java/com/displaynone/acss/ui/scan/QrScanFragment.kt +++ b/app/src/main/java/com/displaynone/acss/ui/scan/QrScanFragment.kt @@ -61,9 +61,7 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) { viewModel.action.collectWhenStarted(this) { action -> when (action) { is QrScanViewModel.Action.RequestPermission -> requestPermission(action.permission) - is QrScanViewModel.Action.CloseWithCancel -> { - goBack() - } + is QrScanViewModel.Action.CloseWithCancel -> goBack() is QrScanViewModel.Action.CloseWithResult -> { sendResult(QrScanDestination.packToBundle(action.result)) goBack() @@ -82,9 +80,7 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) { val previewView: PreviewView = binding.viewFinder val executor = ContextCompat.getMainExecutor(context) - val options = BarcodeScannerOptions.Builder() - .setBarcodeFormats(Barcode.FORMAT_QR_CODE) - .build() + val options = BarcodeScannerOptions.Builder().setBarcodeFormats(Barcode.FORMAT_QR_CODE).build() val barcodeScanner = BarcodeScanning.getClient(options) this.barcodeScanner = barcodeScanner diff --git a/app/src/main/java/com/displaynone/acss/ui/scan/QrScanViewModel.kt b/app/src/main/java/com/displaynone/acss/ui/scan/QrScanViewModel.kt index 6961085..18537f9 100644 --- a/app/src/main/java/com/displaynone/acss/ui/scan/QrScanViewModel.kt +++ b/app/src/main/java/com/displaynone/acss/ui/scan/QrScanViewModel.kt @@ -1,7 +1,5 @@ package com.displaynone.acss.ui.scan -import androidx.lifecycle.ViewModel - import android.Manifest import android.app.Application import android.content.pm.PackageManager @@ -80,6 +78,7 @@ class QrScanViewModel( data class RequestPermission( val permission: String ) : Action + data object CloseWithCancel : Action data class CloseWithResult( val result: String diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 7f9ec69..2a2aca1 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -17,7 +17,7 @@ Date: Thu, 20 Feb 2025 12:19:55 +0300 Subject: [PATCH 3/3] REPAIR and Redesign and cool val login and pasw --- .../displaynone/acss/ui/auth/AuthFragment.kt | 111 +++++++++++++----- app/src/main/res/layout/fragment_profile.xml | 12 +- app/src/main/res/values-en/strings-en.xml | 7 ++ app/src/main/res/values-ru/strings-ru.xml | 26 ++-- app/src/main/res/values/strings.xml | 9 +- 5 files changed, 125 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/displaynone/acss/ui/auth/AuthFragment.kt b/app/src/main/java/com/displaynone/acss/ui/auth/AuthFragment.kt index 8c0dbe2..bf2aef0 100644 --- a/app/src/main/java/com/displaynone/acss/ui/auth/AuthFragment.kt +++ b/app/src/main/java/com/displaynone/acss/ui/auth/AuthFragment.kt @@ -56,18 +56,17 @@ class AuthFragment: Fragment(R.layout.fragment_auth) { @SuppressLint("ResourceAsColor") override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { binding.error.visibility = View.GONE - val username = s.toString() - val valid = isUsernameValid(username) - - if (valid) { - binding.hint.visibility = View.INVISIBLE - }else{ - binding.login.error = getString(R.string.login_hint) - } +// val username = s.toString() +// val valid = isUsernameValid(username) +// +// if (valid) { +// binding.hint.visibility = View.INVISIBLE +// }else{ +// binding.login.error = getString(R.string.login_hint) +// } // binding.hint.visibility = if(valid) View.INVISIBLE else View.VISIBLE - binding.next.isEnabled = valid - val color = if (valid) R.color.primary else R.color.secondary - binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) +// binding.next.isEnabled = valid + } override fun afterTextChanged(s: Editable?) {} @@ -94,37 +93,95 @@ class AuthFragment: Fragment(R.layout.fragment_auth) { // }) } private fun getPasswordValidError(password: String): String { - if (password.length < 8){ return "LenError" } - val letterRegex = Regex("^(?=.*[A-Z]).+$") - if(!letterRegex.matches(password)){ return "UpperCaseError"} - val digitRegex = Regex("^(?=.*\\\\d).+$") - if(!digitRegex.matches(password)){ return "DigitCaseError"} - + if (password.length < 8) { + return "LenError" + } return "NoErrors" } - private fun isUsernameValid(username: String): Boolean { + private fun validatePasswordAndSetError(password: String) { + val errorType = getPasswordValidError(password) + + when (errorType) { + "LenError" -> { + binding.password.error = getString(R.string.error_password_too_short) + } + + "NoErrors" -> { + binding.password.error = null + } + } + } + private fun getLoginValidError(username: String): String { val alf = "^[a-zA-Z0-9_]+$".toRegex() - return username.isNotEmpty() && - username.length >= 3 && - !username[0].isDigit() && - alf.matches(username) - } - private fun isPasswordValid(password: String): Boolean { - return password.isNotEmpty() && - password.length >= 8 + if (username.isEmpty()) { + return "EmptyError" + } + if (username.length < 3) { + return "LenError" + } + if (username[0].isDigit()) { + return "StartsWithDigitError" + } + if (!alf.matches(username)) { + return "InvalidCharactersError" + } + return "NoErrors" } +// private fun isPasswordValid(password: String): Boolean { +// return password.isNotEmpty() && +// password.length >= 8 +// } // private fun subscribe() { // viewModel.state.collectWhenStarted(this) { state -> // binding.login.setOnClickListener(this::onLoginButtonClicked) // } // } +private fun validateLoginAndSetError(username: String) { + val errorType = getLoginValidError(username) + when (errorType) { + "EmptyError" -> { + binding.login.error = getString(R.string.error_login_empty) + } + "LenError" -> { + binding.login.error = getString(R.string.error_login_too_short) + } + "StartsWithDigitError" -> { + binding.login.error = getString(R.string.error_login_starts_with_digit) + } + "InvalidCharactersError" -> { + binding.login.error = getString(R.string.error_login_invalid_characters) + } + "NoErrors" -> { + binding.login.error = null + } + } +} private fun onLoginButtonClicked(view: View) { val login = binding.login.text.toString() val password = binding.password.text.toString() - if (login.isEmpty()) return + if (getPasswordValidError(password) != "NoErrors") { + + + val color = R.color.secondary + binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) + validatePasswordAndSetError(password) + }else{ + val color = R.color.primary + binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) + } +// if (login.isEmpty()) return + if (getLoginValidError(login) != "NoErrors") { + val color = R.color.secondary + binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) + validateLoginAndSetError(login) + }else{ + val color = R.color.primary + binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color) + + } viewModel.login(login, password) } diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml index 7be0e0a..ab89cea 100644 --- a/app/src/main/res/layout/fragment_profile.xml +++ b/app/src/main/res/layout/fragment_profile.xml @@ -36,19 +36,29 @@ android:orientation="vertical" android:gravity="center_horizontal" android:paddingTop="50dp"> + + + Profile Server is unavailable Checking the session employee + The password must contain at least 8 characters + The password must contain at least one uppercase letter + The password must contain at least one digit + The username cannot be empty + Login must contain at least 3 characters + Login cannot start with a digit + Login can contain only letters, numbers, and underscores \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings-ru.xml b/app/src/main/res/values-ru/strings-ru.xml index a2fcece..60f25fa 100644 --- a/app/src/main/res/values-ru/strings-ru.xml +++ b/app/src/main/res/values-ru/strings-ru.xml @@ -1,6 +1,6 @@ - AS + AS Введите логин Введите пароль Здравствуйте! @@ -15,13 +15,19 @@ Логина не существует или неверный ОК Ошибка - Введите логин пользователя - Поиск - Профиль - Изменить права входа с помощью смартфона - Панель администратора - Log in to your account - QR code scanning - Scan result - Checking the session employee + Введите логин пользователя + Поиск + Профиль + Изменить права входа с помощью смартфона + Панель администратора + Профиль + Войдите в свою учетную запись + Сканирование QR-кода + Результат проверки + Проверка сотрудника + Нет связи с интернетом + Сервер недоступен + + + \ 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 7e51d96..e7ff264 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,7 +25,12 @@ QR code scanning Scan result Profile - + The password must contain at least 8 characters + The password must contain at least one uppercase letter + The password must contain at least one digit Checking the session employee - + The username cannot be empty + Login must contain at least 3 characters + Login cannot start with a digit + Login can contain only letters, numbers, and underscores \ No newline at end of file