This commit is contained in:
Izlydov 2025-02-20 12:26:47 +03:00
commit b6ff473ae1
13 changed files with 198 additions and 119 deletions

View File

@ -7,21 +7,25 @@ import androidx.annotation.Nullable;
import androidx.security.crypto.EncryptedSharedPreferences; import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys; 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.displaynone.acss.components.auth.models.user.repository.dto.UserDTO;
import com.google.gson.Gson; import com.google.gson.Gson;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.Optional;
public class UserManager { public class UserManager {
// Preferences
private static final String _PREFERENCES_FILENAME = "userData"; private static final String _PREFERENCES_FILENAME = "userData";
private final SharedPreferences _preferences; 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 UserDTO userDTO;
private static final String ACCESS_KEY = "user";
private Gson gson = new Gson();
public UserManager(Context context) { public UserManager(Context context) {
this._preferences = this._createEncryptedPreferences(context); this._preferences = this._createEncryptedPreferences(context);
@ -40,23 +44,24 @@ public class UserManager {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public void saveDto(UserDTO userDTO) { public void saveDto(UserDTO userDTO) {
this.userDTO = userDTO; this.userDTO = userDTO;
_preferences.edit() _preferences.edit()
.putString(ACCESS_KEY,toJson(userDTO)) .putString(_KEY_USER, toJson(userDTO))
.apply(); .apply();
} }
public @Nullable UserDTO getDto() { public @Nullable UserDTO getDto() {
if (this.userDTO != null) return this.userDTO; if (this.userDTO != null) return this.userDTO;
UserDTO userDTO = fromJson( _preferences.getString(ACCESS_KEY, null)); return fromJson(_preferences.getString(_KEY_USER, 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);
} }
private UserDTO fromJson(String userJSON) {
return gson.fromJson(userJSON, UserDTO.class);
}
private String toJson(UserDTO userDTO) {
return gson.toJson(userDTO);
}
} }

View File

@ -1,5 +1,5 @@
package com.displaynone.acss.config package com.displaynone.acss.config
object Constants { object Constants {
const val serverUrl = "http://192.168.136.38:8086" const val serverUrl = "http://192.168.56.1:8086"
} }

View File

@ -46,7 +46,7 @@ class AdminFragment : Fragment(R.layout.fragment_admin) {
} }
if (state is AdminViewModel.State.Error){ if (state is AdminViewModel.State.Error){
val errorMessage = state.errorMessage val errorMessage = state.errorMessage
binding.loginSearch.setError(errorMessage) binding.loginSearch.error = errorMessage
} }
} }
} }

View File

@ -56,18 +56,17 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
@SuppressLint("ResourceAsColor") @SuppressLint("ResourceAsColor")
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
binding.error.visibility = View.GONE binding.error.visibility = View.GONE
val username = s.toString() // val username = s.toString()
val valid = isUsernameValid(username) // val valid = isUsernameValid(username)
//
if (valid) { // if (valid) {
binding.hint.visibility = View.INVISIBLE // binding.hint.visibility = View.INVISIBLE
}else{ // }else{
binding.login.error = getString(R.string.login_hint) // binding.login.error = getString(R.string.login_hint)
} // }
// binding.hint.visibility = if(valid) View.INVISIBLE else View.VISIBLE // binding.hint.visibility = if(valid) View.INVISIBLE else View.VISIBLE
binding.next.isEnabled = valid // binding.next.isEnabled = valid
val color = if (valid) R.color.primary else R.color.secondary
binding.next.backgroundTintList = ContextCompat.getColorStateList(requireContext(), color)
} }
override fun afterTextChanged(s: Editable?) {} override fun afterTextChanged(s: Editable?) {}
@ -94,37 +93,95 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
// }) // })
} }
private fun getPasswordValidError(password: String): String { private fun getPasswordValidError(password: String): String {
if (password.length < 8){ return "LenError" } if (password.length < 8) {
val letterRegex = Regex("^(?=.*[A-Z]).+$") return "LenError"
if(!letterRegex.matches(password)){ return "UpperCaseError"} }
val digitRegex = Regex("^(?=.*\\\\d).+$")
if(!digitRegex.matches(password)){ return "DigitCaseError"}
return "NoErrors" 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() val alf = "^[a-zA-Z0-9_]+$".toRegex()
return username.isNotEmpty() && if (username.isEmpty()) {
username.length >= 3 && return "EmptyError"
!username[0].isDigit() && }
alf.matches(username) if (username.length < 3) {
} return "LenError"
private fun isPasswordValid(password: String): Boolean { }
return password.isNotEmpty() && if (username[0].isDigit()) {
password.length >= 8 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() { // private fun subscribe() {
// viewModel.state.collectWhenStarted(this) { state -> // viewModel.state.collectWhenStarted(this) { state ->
// binding.login.setOnClickListener(this::onLoginButtonClicked) // 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) { private fun onLoginButtonClicked(view: View) {
val login = binding.login.text.toString() val login = binding.login.text.toString()
val password = binding.password.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) viewModel.login(login, password)
} }

View File

@ -109,4 +109,9 @@ class InitFragment : Fragment(R.layout.fragment_init) {
private fun handleError(errorMessage: String) { private fun handleError(errorMessage: String) {
binding.error.text = errorMessage binding.error.text = errorMessage
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
} }

View File

@ -11,8 +11,8 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.displaynone.acss.R 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.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.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.databinding.FragmentProfileBinding import com.displaynone.acss.databinding.FragmentProfileBinding
import com.displaynone.acss.ui.profile.ProfileViewModel.Action 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.collectWithLifecycle
import com.displaynone.acss.util.navigateTo 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 var _binding: FragmentProfileBinding? = null
private val binding: FragmentProfileBinding get() = _binding!! private val binding: FragmentProfileBinding get() = _binding!!
@ -33,29 +33,28 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) {
checkForAdmin() checkForAdmin()
binding.swipeRefresh.setOnRefreshListener { binding.swipeRefresh.setOnRefreshListener {
if (getIsMe()){ if (getIsMe()) refreshData() else showData(getUserDto()!!);
refreshData()
} else{
showData(getUserDto()!!)
}
} }
binding.logout.setOnClickListener{
binding.logout.setOnClickListener {
logout() logout()
} }
binding.scan.setOnClickListener{
binding.scan.setOnClickListener {
navigateTo(view, R.id.action_profileFragment_to_qrScanFragment) navigateTo(view, R.id.action_profileFragment_to_qrScanFragment)
} }
binding.buttonSearch.setOnClickListener{
binding.buttonSearch.setOnClickListener {
navigateTo(view, R.id.action_profileFragment_to_adminFragment) navigateTo(view, R.id.action_profileFragment_to_adminFragment)
} }
binding.recyclerViewLogs.adapter = adapter binding.recyclerViewLogs.adapter = adapter
if (getIsMe()) { if (getIsMe()) {
refreshData() refreshData()
viewModel.visitListState.collectWithLifecycle(this) { data -> viewModel.visitListState.collectWithLifecycle(this) { data -> adapter.submitData(data) }
adapter.submitData(data)
}
waitForQRScanResult() waitForQRScanResult()
} else{ } else {
showData(getUserDto()!!) showData(getUserDto()!!)
Log.d("ProfileFragment", "set login") Log.d("ProfileFragment", "set login")
@ -67,40 +66,34 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) {
} }
subscribe() subscribe()
binding.recyclerViewLogs.layoutManager = LinearLayoutManager(requireContext()) binding.recyclerViewLogs.layoutManager = LinearLayoutManager(requireContext())
// viewModel.visitListStateFromLogin.collectWithLifecycle(this) { data ->
// adapter.submitData(data)
// }
} }
private fun checkForAdmin() { private fun checkForAdmin() {
Log.d("check", "cheking for roles") val userDTO = UserServiceST.getInstance().getUserDTO() ?: return
val userDTO = UserServiceST.getInstance().getUserDTO() if (userDTO.roles.any { it.name == "ROLE_ADMIN" }) {
if (userDTO != null) { binding.buttonSearch.visibility = View.VISIBLE
if (userDTO.roles.any { it.name == "ROLE_ADMIN" }) { binding.rightsUsingSmartphone.text = "Пропуск действителен"
Log.d("adminlog", "i'm admin") }
binding.buttonSearch.visibility = View.VISIBLE if (userDTO.roles.any { it.name == "ROLE_USER" }) {
binding.rightsUsingSmartphone.text = "Пропуск действителен" binding.rightsUsingSmartphone.text = "Пропуск действителен"
}
if (userDTO.roles.any { it.name == "ROLE_USER" }) {
Log.d("userlog", "i'm user")
binding.rightsUsingSmartphone.text = "Пропуск действителен"
}
} }
} }
private fun hideButtons() { private fun hideButtons() {
binding.logout.visibility = View.GONE binding.logout.visibility = View.GONE
binding.scan.visibility = View.GONE binding.scan.visibility = View.GONE
binding.buttonSearch.visibility = View.GONE binding.buttonSearch.visibility = View.GONE
binding.changeRights.visibility = View.VISIBLE binding.changeRights.visibility = View.VISIBLE
} }
fun showMyData(userDTO: UserDTO){
private fun showMyData(userDTO: UserDTO) {
binding.fio.text = userDTO.name binding.fio.text = userDTO.name
binding.position.text = userDTO.position binding.position.text = userDTO.position
setAvatar(userDTO.photo) setAvatar(userDTO.photo)
} }
fun showData(userDTO: UserDTO){
private fun showData(userDTO: UserDTO) {
binding.fio.text = userDTO.name binding.fio.text = userDTO.name
binding.position.text = userDTO.position binding.position.text = userDTO.position
binding.changeRights.text = if(userDTO.isACSBlocked) "Разблокировать пользователя" else "Заблокировать пользователя" binding.changeRights.text = if(userDTO.isACSBlocked) "Разблокировать пользователя" else "Заблокировать пользователя"
@ -114,30 +107,34 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) {
setAvatar(userDTO.photo) setAvatar(userDTO.photo)
} }
private fun refreshData() { private fun refreshData() {
Log.d("ProfileFragment", "Refreshed")
viewModel.getInfo() viewModel.getInfo()
subscribeToGetData() subscribeToGetData()
} }
fun subscribe() {
private fun subscribe() {
viewModel.action.collectWithLifecycle(this) { action -> viewModel.action.collectWithLifecycle(this) { action ->
if (action is Action.GoToAuth) { 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) { 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? { private fun getUserDto(): UserDTO? {
return arguments?.getSerializable("user") as? UserDTO return arguments?.getSerializable("user") as? UserDTO
} }
private fun getIsMe(): Boolean { private fun getIsMe(): Boolean {
return arguments?.getBoolean("isMe", true) ?: true return arguments?.getBoolean("isMe", true) ?: true
} }
private fun waitForQRScanResult() { private fun waitForQRScanResult() {
requireActivity().onBackPressedDispatcher.addCallback( requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner, viewLifecycleOwner,
object : OnBackPressedCallback(true) { object : OnBackPressedCallback(true) {
@ -151,20 +148,19 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) {
)?.observe(viewLifecycleOwner) { bundle -> )?.observe(viewLifecycleOwner) { bundle ->
val qrCode = bundle.getString("key_qr") val qrCode = bundle.getString("key_qr")
if (!qrCode.isNullOrEmpty()) view?.let { if (!qrCode.isNullOrEmpty()) view?.let {
val bundle = Bundle().apply { val newBundle = Bundle().apply { putString("qrCode", qrCode) }
putString("qrCode", qrCode) navigateTo(it, R.id.action_profileFragment_to_qrResultFragment, newBundle)
}
navigateTo(it, R.id.action_profileFragment_to_qrResultFragment, bundle)
} }
} }
} }
private fun logout() { private fun logout() {
viewModel.logout() viewModel.logout()
viewModel.openAuth() viewModel.openAuth()
Toast.makeText(activity, "LOGOUT", Toast.LENGTH_SHORT).show() Toast.makeText(activity, "LOGOUT", Toast.LENGTH_SHORT).show()
} }
private fun subscribeToGetData(){ private fun subscribeToGetData() {
viewModel.state.collectWithLifecycle(this) { state -> viewModel.state.collectWithLifecycle(this) { state ->
if (state is ProfileViewModel.State.Show) { if (state is ProfileViewModel.State.Show) {
val userDto: UserDTO = state.item val userDto: UserDTO = state.item

View File

@ -1,13 +1,9 @@
package com.displaynone.acss.ui.result package com.displaynone.acss.ui.result
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.View import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.displaynone.acss.R import com.displaynone.acss.R
import com.displaynone.acss.databinding.FragmentQrResultBinding import com.displaynone.acss.databinding.FragmentQrResultBinding
import com.displaynone.acss.util.collectWithLifecycle 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
Log.d("QrResultFragment", getQrCode())
_binding = FragmentQrResultBinding.bind(view) _binding = FragmentQrResultBinding.bind(view)
binding.close.setOnClickListener(this::closeQrScanFragment) binding.close.setOnClickListener(this::closeQrScanFragment)
this.openDoor() this.openDoor()
viewModel.state.collectWithLifecycle(this){ state -> viewModel.state.collectWithLifecycle(this) { state ->
if (state is QrResultViewModel.State.Result){ if (state is QrResultViewModel.State.Result) {
if (state.resultCode == 200) { when (state.resultCode) {
setResult(getString(R.string.success)) 200 -> {
} else if (state.resultCode == 400) { setResult(getString(R.string.success))
setResult(getString(R.string.wrong)) }
} else if (state.resultCode == 401) {
setResult(getString(R.string.cancel)) 400 -> {
setResult(getString(R.string.wrong))
}
401 -> {
setResult(getString(R.string.cancel))
}
} }
} }
if (state is QrResultViewModel.State.Error){ if (state is QrResultViewModel.State.Error) setResult(state.errorMessage)
setResult(state.errorMessage)
}
} }
} }
private fun openDoor() { private fun openDoor() {
val qrValue = getQrCode() ?: return
val qrCodeValueLong: Long val qrCodeValueLong: Long
try { try {
qrCodeValueLong = getQrCode().toLong() qrCodeValueLong = qrValue.toLong()
} catch (exception: Exception) { } catch (exception: Exception) {
when (exception) { when (exception) {
is NumberFormatException, is IllegalArgumentException -> setResult(getString(R.string.wrong)) is NumberFormatException, is IllegalArgumentException -> setResult(getString(R.string.wrong))
@ -60,8 +59,8 @@ class QrResultFragment : Fragment(R.layout.fragment_qr_result) {
viewModel.openDoor(qrCodeValueLong) viewModel.openDoor(qrCodeValueLong)
} }
private fun getQrCode(): String { private fun getQrCode(): String? {
return arguments?.getString("qrCode") ?: "No QR Code Provided" return arguments?.getString("qrCode")
} }
private fun closeQrScanFragment(view: View) { private fun closeQrScanFragment(view: View) {

View File

@ -61,9 +61,7 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
viewModel.action.collectWhenStarted(this) { action -> viewModel.action.collectWhenStarted(this) { action ->
when (action) { when (action) {
is QrScanViewModel.Action.RequestPermission -> requestPermission(action.permission) is QrScanViewModel.Action.RequestPermission -> requestPermission(action.permission)
is QrScanViewModel.Action.CloseWithCancel -> { is QrScanViewModel.Action.CloseWithCancel -> goBack()
goBack()
}
is QrScanViewModel.Action.CloseWithResult -> { is QrScanViewModel.Action.CloseWithResult -> {
sendResult(QrScanDestination.packToBundle(action.result)) sendResult(QrScanDestination.packToBundle(action.result))
goBack() goBack()
@ -82,9 +80,7 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
val previewView: PreviewView = binding.viewFinder val previewView: PreviewView = binding.viewFinder
val executor = ContextCompat.getMainExecutor(context) val executor = ContextCompat.getMainExecutor(context)
val options = BarcodeScannerOptions.Builder() val options = BarcodeScannerOptions.Builder().setBarcodeFormats(Barcode.FORMAT_QR_CODE).build()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
.build()
val barcodeScanner = BarcodeScanning.getClient(options) val barcodeScanner = BarcodeScanning.getClient(options)
this.barcodeScanner = barcodeScanner this.barcodeScanner = barcodeScanner

View File

@ -1,7 +1,5 @@
package com.displaynone.acss.ui.scan package com.displaynone.acss.ui.scan
import androidx.lifecycle.ViewModel
import android.Manifest import android.Manifest
import android.app.Application import android.app.Application
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -80,6 +78,7 @@ class QrScanViewModel(
data class RequestPermission( data class RequestPermission(
val permission: String val permission: String
) : Action ) : Action
data object CloseWithCancel : Action data object CloseWithCancel : Action
data class CloseWithResult( data class CloseWithResult(
val result: String val result: String

View File

@ -36,19 +36,29 @@
android:orientation="vertical" android:orientation="vertical"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:paddingTop="50dp"> android:paddingTop="50dp">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="80dp"
android:layout_margin="5dp"
android:elevation="10dp">
<ImageView <ImageView
android:layout_margin="10dp" android:layout_margin="10dp"
android:id="@+id/avatar" android:id="@+id/avatar"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_width="match_parent" android:layout_width="150dp"
android:layout_height="150dp" android:layout_height="150dp"
android:foregroundGravity="center" android:foregroundGravity="center"
android:importantForAccessibility="no" android:importantForAccessibility="no"
android:src="@drawable/_user_" android:src="@drawable/_user_"
app:layout_constraintBottom_toTopOf="@+id/title" app:layout_constraintBottom_toTopOf="@+id/title"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
</androidx.cardview.widget.CardView>
<androidx.constraintlayout.helper.widget.Flow <androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow" android:id="@+id/flow"

View File

@ -17,7 +17,7 @@
<fragment <fragment
android:id="@+id/nav_profile" android:id="@+id/nav_profile"
android:name="com.displaynone.acss.ui.profile.ProfileFragment" android:name="com.displaynone.acss.ui.profile.ProfileFragment"
android:label="@string/profile" android:label="@string/title_profile"
tools:layout="@layout/fragment_profile"> tools:layout="@layout/fragment_profile">
<action <action
android:id="@+id/action_profileFragment_to_authFragment" android:id="@+id/action_profileFragment_to_authFragment"

View File

@ -26,4 +26,11 @@
<string name="profile">Profile</string> <string name="profile">Profile</string>
<string name="serverIsUnabailable">Server is unavailable</string> <string name="serverIsUnabailable">Server is unavailable</string>
<string name="AdminFragment">Checking the session employee</string> <string name="AdminFragment">Checking the session employee</string>
<string name="error_password_too_short">The password must contain at least 8 characters</string>
<string name="error_password_no_uppercase">The password must contain at least one uppercase letter</string>
<string name="error_password_no_digit">The password must contain at least one digit</string>
<string name="error_login_empty">The username cannot be empty</string>
<string name="error_login_too_short">Login must contain at least 3 characters</string>
<string name="error_login_starts_with_digit">Login cannot start with a digit</string>
<string name="error_login_invalid_characters">Login can contain only letters, numbers, and underscores</string>
</resources> </resources>

View File

@ -25,7 +25,12 @@
<string name="qrScanFragment">QR code scanning</string> <string name="qrScanFragment">QR code scanning</string>
<string name="qrResultFragment">Scan result</string> <string name="qrResultFragment">Scan result</string>
<string name="profile">Profile</string> <string name="profile">Profile</string>
<string name="error_password_too_short">The password must contain at least 8 characters</string>
<string name="error_password_no_uppercase">The password must contain at least one uppercase letter</string>
<string name="error_password_no_digit">The password must contain at least one digit</string>
<string name="AdminFragment">Checking the session employee</string> <string name="AdminFragment">Checking the session employee</string>
<string name="error_login_empty">The username cannot be empty</string>
<string name="error_login_too_short">Login must contain at least 3 characters</string>
<string name="error_login_starts_with_digit">Login cannot start with a digit</string>
<string name="error_login_invalid_characters">Login can contain only letters, numbers, and underscores</string>
</resources> </resources>