fix authorization, add profile layout

This commit is contained in:
varksyu 2025-02-19 11:30:24 +03:00
parent e2f43cb936
commit 5840424b91
13 changed files with 291 additions and 39 deletions

View File

@ -1,12 +1,12 @@
package ru.myitschool.work.data.auth package ru.myitschool.work.data.auth
import ru.myitschool.work.data.user.UserDto import ru.myitschool.work.data.user.UserDto
import ru.sicampus.bootcamp2025.domain.auth.AuthRepo import ru.myitschool.work.domain.auth.AuthRepo
class AuthRepoImpl( class AuthRepoImpl(
private val authNetworkDataSource: AuthNetworkDataSource, private val authNetworkDataSource: AuthNetworkDataSource,
private val authStorageDataSource: AuthStorageDataSource private val authStorageDataSource: AuthStorageDataSource
) : AuthRepo{ ) : AuthRepo {
override suspend fun isUserExist(email: String): Result<Boolean?> { override suspend fun isUserExist(email: String): Result<Boolean?> {
return authNetworkDataSource.isUserExist(email) return authNetworkDataSource.isUserExist(email)
} }

View File

@ -12,8 +12,6 @@ data class UserDto(
val id : Long?, val id : Long?,
@SerialName("login") @SerialName("login")
var login: String, var login: String,
@SerialName("birthDate")
var birthDate : String?,
@SerialName("name") @SerialName("name")
var name: String, var name: String,
@SerialName("avatarUrl") @SerialName("avatarUrl")

View File

@ -0,0 +1,8 @@
package ru.myitschool.work.domain.auth
import ru.myitschool.work.data.user.UserDto
interface AuthRepo {
suspend fun isUserExist(email: String): Result<Boolean?>
suspend fun login(email: String, password: String) : Result <UserDto>
}

View File

@ -0,0 +1,12 @@
package ru.myitschool.work.domain.auth
import javax.inject.Inject
class IsUserExistUseCase (
private val authRepo : AuthRepo
) {
suspend operator fun invoke(email : String) : Result<Boolean?> {
return authRepo.isUserExist(email)
}
}

View File

@ -0,0 +1,11 @@
package ru.myitschool.work.domain.auth
import ru.myitschool.work.data.user.UserDto
class LoginUseCase(
private val authRepo : AuthRepo
){
suspend operator fun invoke(email : String, password : String) : Result<UserDto> {
return authRepo.login(email, password)
}
}

View File

@ -0,0 +1,18 @@
package ru.sicampus.bootcamp2025.data
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
object Network {
val client = HttpClient(CIO) {
install(ContentNegotiation) {
json(Json {
isLenient = true
ignoreUnknownKeys = true
})
}
}
}

View File

@ -11,7 +11,9 @@ import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import ru.myitschool.work.R import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentLoginBinding import ru.myitschool.work.databinding.FragmentLoginBinding
import ru.myitschool.work.ui.RootActivity
import ru.myitschool.work.utils.collectWhenStarted import ru.myitschool.work.utils.collectWhenStarted
import ru.myitschool.work.utils.collectWithLifecycle
import ru.myitschool.work.utils.visibleOrGone import ru.myitschool.work.utils.visibleOrGone
@AndroidEntryPoint @AndroidEntryPoint
@ -19,17 +21,17 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
private var _viewBinding: FragmentLoginBinding? = null private var _viewBinding: FragmentLoginBinding? = null
private val viewBinding: FragmentLoginBinding get() = _viewBinding!! private val viewBinding: FragmentLoginBinding get() = _viewBinding!!
private val viewModel: LoginViewModel by viewModels() private val viewModel: LoginViewModel by viewModels() {LoginViewModel.Factory}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_viewBinding = FragmentLoginBinding.bind(view) _viewBinding = FragmentLoginBinding.bind(view)
/*viewBinding.signInButton.setOnClickListener { viewBinding.signInButton.setOnClickListener {
val login = viewBinding.userLogin.text.toString() val login = viewBinding.userLogin.text.toString()
val password = viewBinding.userPassword.text.toString() val password = viewBinding.userPassword.text.toString()
if (!isValidEmail(login)) { if (!isValidLogin(login)) {
viewBinding.errorText.text = getString(R.string.error_valid) viewBinding.errorText.text = getString(R.string.error_valid)
viewBinding.errorText.visibility = View.VISIBLE viewBinding.errorText.visibility = View.VISIBLE
} }
@ -38,13 +40,13 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
viewBinding.errorText.visibility = View.VISIBLE viewBinding.errorText.visibility = View.VISIBLE
} }
else { else {
viewModel.auth(email, password) viewModel.auth(login, password)
viewBinding.errorText.visibility = View.GONE viewBinding.errorText.visibility = View.GONE
} }
} }
viewModel.state.collectWithLifecycle(this) { state -> viewModel.state.collectWithLifecycle(this) { state ->
if (state is AuthViewModel.State.Show) { if (state is LoginViewModel.State.Show) {
viewBinding.errorText.text = state.errorText.toString() viewBinding.errorText.text = state.errorText.toString()
viewBinding.errorText.visibility = viewBinding.errorText.visibility =
if (state.errorText == null) View.GONE else View.VISIBLE if (state.errorText == null) View.GONE else View.VISIBLE
@ -52,7 +54,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
} }
viewModel.navigateToMain.collectWithLifecycle(viewLifecycleOwner) { userRole -> viewModel.navigateToMain.collectWithLifecycle(viewLifecycleOwner) { userRole ->
val intent = Intent(requireContext(), MainActivity::class.java).apply { val intent = Intent(requireContext(), RootActivity::class.java).apply {
putExtra("USER_ROLE", userRole) putExtra("USER_ROLE", userRole)
} }
startActivity(intent) startActivity(intent)
@ -66,10 +68,10 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
} }
})*/ })
} }
private fun isValidEmail(email: String): Boolean { private fun isValidLogin(email: String): Boolean {
return Patterns.EMAIL_ADDRESS.matcher(email).matches() return true //Patterns.EMAIL_ADDRESS.matcher(email).matches()
} }
private fun isValidPassword(password : String) : Boolean { private fun isValidPassword(password : String) : Boolean {
return password.length >= 8 return password.length >= 8

View File

@ -1,10 +1,11 @@
package ru.myitschool.work.ui.login package ru.myitschool.work.ui.login
import LoginUseCase
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@ -13,16 +14,20 @@ import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.myitschool.work.R import ru.myitschool.work.R
import ru.myitschool.work.data.auth.AuthNetworkDataSource
import ru.myitschool.work.data.auth.AuthRepoImpl
import ru.myitschool.work.data.auth.AuthStorageDataSource
import ru.myitschool.work.domain.auth.IsUserExistUseCase import ru.myitschool.work.domain.auth.IsUserExistUseCase
import ru.myitschool.work.domain.auth.LoginUseCase
import javax.inject.Inject import javax.inject.Inject
import kotlin.reflect.KClass
@HiltViewModel class LoginViewModel constructor(
class LoginViewModel @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val isUserExistUseCase: IsUserExistUseCase, private val isUserExistUseCase: IsUserExistUseCase,
private val loginUseCase: LoginUseCase private val loginUseCase: LoginUseCase
) : ViewModel() { ) : ViewModel() {
private val _state = MutableStateFlow(true) private val _state = MutableStateFlow<State>(getStateShow())
val state = _state.asStateFlow() val state = _state.asStateFlow()
private val _navigateToMain = MutableSharedFlow<String?>() private val _navigateToMain = MutableSharedFlow<String?>()
@ -31,7 +36,7 @@ class LoginViewModel @Inject constructor(
private val _userRole = MutableSharedFlow<String>() private val _userRole = MutableSharedFlow<String>()
val userRole = _userRole.asSharedFlow() val userRole = _userRole.asSharedFlow()
/*
init { init {
viewModelScope.launch { viewModelScope.launch {
updateState() updateState()
@ -39,15 +44,14 @@ class LoginViewModel @Inject constructor(
} }
fun auth( fun auth(
email : String, login : String,
password : String password : String
) )
{ {
viewModelScope.launch { viewModelScope.launch {
_state.emit(State.Loading) when (checkUserExistence(login)) {
when (checkUserExistence(email)) {
true -> { true -> {
loginUser(email, password) loginUser(login, password)
} }
false -> { false -> {
updateState(context.getString(R.string.error_invalid_credentials)) updateState(context.getString(R.string.error_invalid_credentials))
@ -63,12 +67,12 @@ class LoginViewModel @Inject constructor(
result.fold( result.fold(
onSuccess = {isExist -> isExist}, onSuccess = {isExist -> isExist},
onFailure = { onFailure = {
Log.e("AuthViewModel", "Error checking user existence", it) Log.e("LoginViewModel", "Error checking user existence", it)
null null
} }
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e("AuthViewModel", "Error during user existence check", e) Log.e("LoginViewModel", "Error during user existence check", e)
null null
} }
} }
@ -105,28 +109,27 @@ class LoginViewModel @Inject constructor(
} }
sealed interface State { sealed interface State {
data object Loading : State
data class Show( data class Show(
var errorText : String? var errorText : String?
) : State ) : State
} }
*/
/*companion object {
companion object {
val Factory : ViewModelProvider.Factory = object : ViewModelProvider.Factory { val Factory : ViewModelProvider.Factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: KClass<T>, extras: CreationExtras): T { override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
val application = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]!! val application = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]!!
val authRepoImpl = AuthRepoImpl( val authRepoImpl = AuthRepoImpl(
authNetworkDataSource = AuthNetworkDataSource, authNetworkDataSource = AuthNetworkDataSource,
authStorageDataSource = AuthStorageDataSource authStorageDataSource = AuthStorageDataSource
) )
return AuthViewModel( return LoginViewModel(
application = application, application,
isUserExistUseCase = IsUserExistUseCase(authRepoImpl), isUserExistUseCase = IsUserExistUseCase(authRepoImpl),
loginUseCase = LoginUseCase(authRepoImpl) loginUseCase = LoginUseCase(authRepoImpl)
) as T ) as T
} }
} }
}*/ }
} }

View File

@ -1,8 +1,11 @@
package ru.myitschool.work.utils package ru.myitschool.work.utils
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -15,4 +18,14 @@ inline fun <T> Flow<T>.collectWhenStarted(
collector.invoke(value) collector.invoke(value)
} }
} }
}
fun <T> Flow<T>.collectWithLifecycle(
lifecycleOwner: LifecycleOwner,
function: (T) -> Unit
){
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
collect { function.invoke(it) }
}
}
} }

View File

@ -1,5 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="41dp"
<path android:fillColor="@android:color/white" android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/> android:height="41dp"
android:viewportWidth="41"
android:viewportHeight="41">
<path
android:pathData="M19.219,17.938H5.766C5.596,17.938 5.433,17.87 5.313,17.75C5.192,17.63 5.125,17.467 5.125,17.297C5.125,17.127 5.192,16.964 5.313,16.844C5.433,16.724 5.596,16.656 5.766,16.656H19.219C19.389,16.656 19.552,16.724 19.672,16.844C19.792,16.964 19.859,17.127 19.859,17.297C19.859,17.467 19.792,17.63 19.672,17.75C19.552,17.87 19.389,17.938 19.219,17.938Z"
android:fillColor="#FF6900"/>
<path
android:pathData="M23.063,36.516C22.943,36.516 22.825,36.483 22.723,36.419C22.631,36.362 22.555,36.282 22.502,36.186C22.449,36.091 22.422,35.984 22.422,35.875V11.531C22.422,11.41 22.457,11.292 22.521,11.19C22.586,11.088 22.678,11.007 22.787,10.955L33.735,5.766H28.828C28.658,5.766 28.495,5.698 28.375,5.578C28.255,5.458 28.188,5.295 28.188,5.125C28.188,4.955 28.255,4.792 28.375,4.672C28.495,4.552 28.658,4.484 28.828,4.484H34.594C35.104,4.484 35.592,4.687 35.953,5.047C36.313,5.408 36.516,5.897 36.516,6.406V28.674C36.516,29.032 36.417,29.383 36.229,29.687C36.041,29.992 35.772,30.238 35.452,30.398L23.351,36.445C23.261,36.491 23.163,36.515 23.063,36.516ZM23.703,11.935V34.837L34.882,29.251C34.989,29.197 35.078,29.115 35.14,29.013C35.203,28.911 35.235,28.794 35.234,28.674V6.477L23.703,11.935Z"
android:fillColor="#FF6900"/>
<path
android:pathData="M10.891,23.703C10.806,23.704 10.723,23.687 10.645,23.656C10.567,23.624 10.496,23.577 10.436,23.517L4.67,17.752C4.61,17.692 4.562,17.621 4.53,17.543C4.497,17.465 4.481,17.381 4.481,17.297C4.481,17.212 4.497,17.128 4.53,17.05C4.562,16.972 4.61,16.902 4.67,16.842L10.436,11.076C10.556,10.956 10.72,10.888 10.891,10.888C11.061,10.888 11.225,10.956 11.345,11.076C11.466,11.197 11.534,11.361 11.534,11.531C11.534,11.702 11.466,11.865 11.345,11.986L6.028,17.297L11.345,22.608C11.406,22.667 11.453,22.738 11.486,22.816C11.518,22.894 11.535,22.978 11.535,23.062C11.535,23.147 11.518,23.231 11.486,23.309C11.453,23.387 11.406,23.458 11.345,23.517C11.286,23.577 11.215,23.624 11.137,23.656C11.059,23.687 10.975,23.704 10.891,23.703Z"
android:fillColor="#FF6900"/>
<path
android:pathData="M23.063,30.109H17.297C16.787,30.109 16.298,29.907 15.938,29.546C15.578,29.186 15.375,28.697 15.375,28.188V23.063C15.375,22.893 15.443,22.73 15.563,22.61C15.683,22.489 15.846,22.422 16.016,22.422C16.185,22.422 16.348,22.489 16.469,22.61C16.589,22.73 16.656,22.893 16.656,23.063V28.188C16.656,28.357 16.724,28.52 16.844,28.64C16.964,28.761 17.127,28.828 17.297,28.828H23.063C23.232,28.828 23.395,28.896 23.515,29.016C23.636,29.136 23.703,29.299 23.703,29.469C23.703,29.639 23.636,29.802 23.515,29.922C23.395,30.042 23.232,30.109 23.063,30.109Z"
android:fillColor="#FF6900"/>
<path
android:pathData="M16.016,12.172C15.846,12.172 15.683,12.104 15.563,11.984C15.443,11.864 15.375,11.701 15.375,11.531V6.406C15.375,5.897 15.578,5.408 15.938,5.047C16.298,4.687 16.787,4.484 17.297,4.484H31.391C31.56,4.484 31.723,4.552 31.844,4.672C31.964,4.792 32.031,4.955 32.031,5.125C32.031,5.295 31.964,5.458 31.844,5.578C31.723,5.698 31.56,5.766 31.391,5.766H17.297C17.127,5.766 16.964,5.833 16.844,5.953C16.724,6.073 16.656,6.236 16.656,6.406V11.531C16.656,11.701 16.589,11.864 16.469,11.984C16.348,12.104 16.185,12.172 16.016,12.172Z"
android:fillColor="#FF6900"/>
</vector> </vector>

View File

@ -1,5 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
<path android:fillColor="@android:color/white" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/> android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M25.905,14.702C26.347,14.702 26.708,15.061 26.678,15.502C26.506,18.069 25.504,20.524 23.811,22.516C21.943,24.714 19.349,26.212 16.475,26.764C13.601,27.316 10.617,26.889 8.029,25.554C5.44,24.218 3.404,22.052 2.274,19.42C1.144,16.786 0.994,13.853 1.852,11.123C2.71,8.394 4.518,6.045 6.961,4.469C9.402,2.894 12.33,2.187 15.245,2.464C17.447,2.674 19.543,3.435 21.334,4.66L20.639,2.158C20.507,1.679 20.789,1.19 21.27,1.066L21.411,1.029C21.892,0.905 22.39,1.192 22.523,1.671L23.935,6.756C23.963,6.86 23.973,6.964 23.965,7.064C23.951,7.444 23.692,7.784 23.302,7.885L17.98,9.262C17.499,9.386 17.001,9.099 16.868,8.62L16.845,8.536C16.712,8.057 16.994,7.568 17.476,7.443L20.64,6.625C19.042,5.409 17.106,4.65 15.056,4.455C12.583,4.22 10.105,4.821 8.045,6.15C5.985,7.478 4.474,9.45 3.76,11.723C3.046,13.995 3.17,16.435 4.112,18.631C5.054,20.827 6.76,22.648 8.946,23.776C11.133,24.905 13.661,25.268 16.098,24.8C18.534,24.332 20.72,23.064 22.287,21.221C23.677,19.586 24.504,17.588 24.673,15.501C24.708,15.061 25.063,14.702 25.505,14.702H25.905Z"
android:fillColor="@color/orange"
android:fillType="evenOdd"/>
</vector> </vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="41dp"
android:height="41dp"
android:viewportWidth="41"
android:viewportHeight="41">
<path
android:pathData="M37.393,23.063C36.156,31.282 29.064,37.583 20.5,37.583C11.065,37.583 3.417,29.935 3.417,20.5C3.417,11.065 11.065,3.417 20.5,3.417C27.505,3.417 33.526,7.633 36.162,13.667"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#FF6900"
android:strokeLineCap="round"/>
<path
android:pathData="M29.042,13.667H36.558C37.124,13.667 37.583,13.208 37.583,12.642V5.125"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#FF6900"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/logout"
android:elevation="20dp"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="18dp"
android:src="@drawable/ic_logout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:clickable="true"
android:focusable="true">
</ImageView>
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/error"
android:layout_width="160dp"
android:layout_height="160dp"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/errorText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:gravity="center"
tools:text="Ошибка помогите" />
<Button
android:id="@+id/refresh"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_gravity="center"
android:background="@drawable/ic_refresh"
android:gravity="center" />
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/profile"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="60dp"
android:src="@drawable/ic_profile"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/refresh_for_profile"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginTop="88dp"
android:layout_marginEnd="18dp"
android:clickable="true"
android:elevation="20dp"
android:focusable="true"
android:src="@drawable/ic_refresh_profile"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:textColor="@color/black"
android:textSize="18dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
tools:text="Иванова Вера Павловна" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="20dp"
android:textColor="@color/black"
android:textSize="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/name"
tools:text="Должность: Разработчик" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:textColor="@color/black"
android:textSize="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
tools:text="Время последнего входа: 12:00 16.09" />
<com.google.android.material.button.MaterialButton
android:layout_width="80dp"
android:layout_height="90dp"
android:layout_marginTop="148dp"
android:backgroundTint="@color/orange"
app:icon="@drawable/ic_qr_code"
app:iconSize="50dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>