merge
This commit is contained in:
		
						commit
						b6ff473ae1
					
				| @ -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; | ||||
|         return fromJson(_preferences.getString(_KEY_USER, null)); | ||||
|     } | ||||
| 
 | ||||
|     private UserDTO fromJson(String userJSON) { | ||||
|         UserDTO userDTO = gson.fromJson(userJSON, UserDTO.class); | ||||
|         return userDTO; | ||||
|         return gson.fromJson(userJSON, UserDTO.class); | ||||
|     } | ||||
| 
 | ||||
|     private String toJson(UserDTO userDTO) { | ||||
|         return gson.toJson(userDTO); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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" | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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) | ||||
|         if (username.isEmpty()) { | ||||
|             return "EmptyError" | ||||
|         } | ||||
|     private fun isPasswordValid(password: String): Boolean { | ||||
|         return password.isNotEmpty() && | ||||
|                 password.length >= 8 | ||||
|         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) | ||||
|     } | ||||
|  | ||||
| @ -109,4 +109,9 @@ class InitFragment : Fragment(R.layout.fragment_init) { | ||||
|     private fun handleError(errorMessage: String) { | ||||
|         binding.error.text = errorMessage | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroyView() { | ||||
|         super.onDestroyView() | ||||
|         _binding = null | ||||
|     } | ||||
| } | ||||
| @ -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 | ||||
| @ -33,27 +33,26 @@ 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 { | ||||
|             logout() | ||||
|         } | ||||
| 
 | ||||
|         binding.scan.setOnClickListener { | ||||
|             navigateTo(view, R.id.action_profileFragment_to_qrScanFragment) | ||||
|         } | ||||
| 
 | ||||
|         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 { | ||||
|             showData(getUserDto()!!) | ||||
| @ -67,40 +66,34 @@ 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() | ||||
|         if (userDTO != null) { | ||||
|     private fun checkForAdmin() { | ||||
|         val userDTO = UserServiceST.getInstance().getUserDTO() ?: return | ||||
| 
 | ||||
|         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 = "Пропуск действителен" | ||||
|         } | ||||
|     } | ||||
|     } | ||||
| 
 | ||||
|     private fun hideButtons() { | ||||
|         binding.logout.visibility = View.GONE | ||||
|         binding.scan.visibility = View.GONE | ||||
|         binding.buttonSearch.visibility = View.GONE | ||||
|         binding.changeRights.visibility = View.VISIBLE | ||||
|     } | ||||
|     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 | ||||
|         binding.changeRights.text = if(userDTO.isACSBlocked) "Разблокировать пользователя" else "Заблокировать пользователя" | ||||
| @ -114,30 +107,34 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) { | ||||
| 
 | ||||
|         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) { | ||||
| @ -151,13 +148,12 @@ 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() | ||||
|  | ||||
| @ -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,7 +17,6 @@ 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) | ||||
| 
 | ||||
| @ -29,26 +24,30 @@ class QrResultFragment : Fragment(R.layout.fragment_qr_result) { | ||||
| 
 | ||||
|         viewModel.state.collectWithLifecycle(this) { state -> | ||||
|             if (state is QrResultViewModel.State.Result) { | ||||
|                 if (state.resultCode == 200) { | ||||
|                 when (state.resultCode) { | ||||
|                     200 -> { | ||||
|                         setResult(getString(R.string.success)) | ||||
|                 } else if (state.resultCode == 400) { | ||||
|                     } | ||||
| 
 | ||||
|                     400 -> { | ||||
|                         setResult(getString(R.string.wrong)) | ||||
|                 } else if (state.resultCode == 401) { | ||||
|                     } | ||||
| 
 | ||||
|                     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) { | ||||
|  | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -36,19 +36,29 @@ | ||||
|                 android:orientation="vertical" | ||||
|                 android:gravity="center_horizontal" | ||||
|                 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 | ||||
|                     android:layout_margin="10dp" | ||||
|                     android:id="@+id/avatar" | ||||
|                     android:layout_marginTop="10dp" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_width="150dp" | ||||
|                     android:layout_height="150dp" | ||||
|                     android:foregroundGravity="center" | ||||
|                     android:importantForAccessibility="no" | ||||
|                     android:src="@drawable/_user_" | ||||
| 
 | ||||
| 
 | ||||
|                     app:layout_constraintBottom_toTopOf="@+id/title" | ||||
|                     app:layout_constraintTop_toTopOf="parent" /> | ||||
|                 </androidx.cardview.widget.CardView> | ||||
| 
 | ||||
|                 <androidx.constraintlayout.helper.widget.Flow | ||||
|                     android:id="@+id/flow" | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
|     <fragment | ||||
|         android:id="@+id/nav_profile" | ||||
|         android:name="com.displaynone.acss.ui.profile.ProfileFragment" | ||||
|         android:label="@string/profile" | ||||
|         android:label="@string/title_profile" | ||||
|         tools:layout="@layout/fragment_profile"> | ||||
|         <action | ||||
|             android:id="@+id/action_profileFragment_to_authFragment" | ||||
|  | ||||
| @ -26,4 +26,11 @@ | ||||
|     <string name="profile">Profile</string> | ||||
|     <string name="serverIsUnabailable">Server is unavailable</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> | ||||
| @ -25,7 +25,12 @@ | ||||
|     <string name="qrScanFragment">QR code scanning</string> | ||||
|     <string name="qrResultFragment">Scan result</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="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> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user