Merge remote-tracking branch 'origin/main'

This commit is contained in:
veronicagtea 2025-02-20 16:08:53 +03:00
commit efd651bd6d
30 changed files with 422 additions and 1186 deletions

View File

@ -48,7 +48,13 @@ dependencies {
implementation(Dependencies.Retrofit.gsonConverter)
implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
implementation("com.squareup.picasso:picasso:2.8")
// Убираем Picasso
// implementation("com.squareup.picasso:picasso:2.8")
// Добавляем Glide
implementation("com.github.bumptech.glide:glide:4.14.2")
kapt("com.github.bumptech.glide:compiler:4.14.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
implementation("androidx.datastore:datastore-preferences:1.1.2")
implementation("com.google.mlkit:barcode-scanning:17.3.0")

View File

@ -6,6 +6,7 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.hilt.android.qualifiers.ApplicationContext
import ru.myitschool.work.api.admin.ApiServiceAdmin
import ru.myitschool.work.api.list.ApiServiceList
import ru.myitschool.work.api.login.ApiServiceLogin
import ru.myitschool.work.api.main.ApiServiceMain
@ -45,4 +46,10 @@ object NetworkModule {
fun provideListApiService(retrofitClient: RetrofitClient): ApiServiceList {
return retrofitClient.getApiServiceList()
}
@Provides
@Singleton
fun provideApiServiceAdmin(retrofitClient: RetrofitClient): ApiServiceAdmin {
return retrofitClient.getApiServiceAdmin()
}
}

View File

@ -1,11 +1,11 @@
package ru.myitschool.work.api
import android.content.Context
import com.google.android.datatransport.BuildConfig
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import ru.myitschool.work.api.admin.ApiServiceAdmin
import ru.myitschool.work.api.list.ApiServiceList
import ru.myitschool.work.api.login.ApiServiceLogin
import ru.myitschool.work.api.main.ApiServiceMain
@ -57,4 +57,9 @@ class RetrofitClient(context: Context) {
fun getApiServiceList(): ApiServiceList {
return retrofitWithAuth.create(ApiServiceList::class.java)
}
fun getApiServiceAdmin(): ApiServiceAdmin {
return retrofitWithAuth.create(ApiServiceAdmin::class.java)
}
}

View File

@ -0,0 +1,7 @@
package ru.myitschool.work.api.admin
import com.google.gson.annotations.SerializedName
class AdminJson(
@SerializedName("authority") var authority: String? = null,
)

View File

@ -0,0 +1,14 @@
package ru.myitschool.work.api.admin
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.PATCH
import retrofit2.http.Path
interface ApiServiceAdmin {
@PATCH("api/admin/authority/change/{login}")
suspend fun block(
@Path("login") login: String,
@Body data: AdminJson
): Response<Void>
}

View File

@ -5,6 +5,6 @@ import retrofit2.http.GET
import retrofit2.http.Path
interface ApiServiceList {
@GET("api/list/{login}")
@GET("api/user/list/{login}")
suspend fun getList(@Path("login") login: String): Response<List<ListInfo>>
}

View File

@ -1,13 +1,9 @@
package ru.myitschool.work.api.login
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
import ru.myitschool.work.api.main.UserInfo
interface ApiServiceLogin {
@GET("api/login")
@GET("api/user/login")
fun authenticate(): Call<Void>
}

View File

@ -5,6 +5,6 @@ import retrofit2.http.GET
import retrofit2.http.Path
interface ApiServiceMain {
@GET("api/info/{login}")
@GET("api/user/info/{login}")
fun getDataUser(@Path("login") login: String): Call<UserInfo>
}

View File

@ -9,15 +9,16 @@ data class UserInfo(
@SerializedName("photo") val photoUrl: String,
@SerializedName("position") val position: String,
@SerializedName("lastVisit") val lastVisit: String,
) {
companion object {
val Initial = UserInfo(
id = 0,
login = "",
name = "",
photoUrl = "",
position = "",
lastVisit = "",
)
}
@SerializedName("authority") val authority: List<AuthorityData>
)
data class AuthorityData(
@SerializedName("id") val id: Int,
@SerializedName("authority") val authority: String,
)
object Authorities {
const val ROLE_ADMIN = "ROLE_ADMIN"
const val ROLE_USER = "ROLE_USER"
const val ROLE_BLOCK = "ROLE_BLOCK"
}

View File

@ -6,6 +6,6 @@ import retrofit2.http.POST
import retrofit2.http.Path
interface ApiServiceScan {
@POST("api/add/{login}")
@POST("api/user/add/{login}")
fun open(@Path("login") login: String, @Body data: CodeJson): Call<Void>
}

View File

@ -9,6 +9,8 @@ import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.fragment
import dagger.hilt.android.AndroidEntryPoint
import ru.myitschool.work.R
import ru.myitschool.work.ui.admin.AdminDestination
import ru.myitschool.work.ui.admin.AdminFragment
import ru.myitschool.work.ui.login.LoginDestination
import ru.myitschool.work.ui.login.LoginFragment
import ru.myitschool.work.ui.main.MainDestination
@ -36,6 +38,7 @@ class RootActivity : AppCompatActivity() {
fragment<QrScanFragment, QrScanDestination>()
fragment<MainFragment, MainDestination>()
fragment<ResultFragment, ResultDestination>()
fragment<AdminFragment, AdminDestination>()
}
}

View File

@ -0,0 +1,6 @@
package ru.myitschool.work.ui.admin
import kotlinx.serialization.Serializable
@Serializable
data object AdminDestination

View File

@ -0,0 +1,101 @@
package ru.myitschool.work.ui.admin
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentAdminBinding
import ru.myitschool.work.ui.main.MainDestination
import ru.myitschool.work.utils.AuthPreferences
import ru.myitschool.work.utils.QrPreferences
import ru.myitschool.work.utils.collectWhenStarted
import ru.myitschool.work.utils.visibleOrGone
@AndroidEntryPoint
class AdminFragment : Fragment(R.layout.fragment_admin) {
private var _binding: FragmentAdminBinding? = null
private val binding: FragmentAdminBinding get() = _binding!!
private val viewModel: AdminViewModel by viewModels()
private lateinit var authPreferences: AuthPreferences
private lateinit var qrPreferences: QrPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
authPreferences = AuthPreferences(requireContext())
qrPreferences = QrPreferences(requireContext())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentAdminBinding.bind(view)
setupOpenComponents()
observeOpenState()
}
private fun setupOpenComponents() {
binding.apply {
block.setOnClickListener {
authPreferences.getLogin().toString().let {
viewModel.block(
it,
true
)
}
}
}
}
private fun observeOpenState() {
viewModel.state.collectWhenStarted(this) { state ->
when (state) {
is AdminViewModel.ResultState.Loading -> {
}
is AdminViewModel.ResultState.Success -> {
}
is AdminViewModel.ResultState.InvalidCredentials -> {
}
is AdminViewModel.ResultState.Error -> {
}
AdminViewModel.ResultState.Initial -> {
}
}
}
}
private fun visibleAll(isVisible: Boolean) {
binding.apply {
}
}
private fun navigateToMainScreen() {
try {
findNavController().apply {
popBackStack(MainDestination, false)
navigate(MainDestination)
}
} catch (e: Exception) {
Log.e("ResultFragment", "Navigation error", e)
Toast.makeText(context, getText(R.string.errorGoText).toString(), Toast.LENGTH_SHORT).show()
}
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
}

View File

@ -0,0 +1,135 @@
package ru.myitschool.work.ui.admin
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import ru.myitschool.work.api.admin.AdminJson
import ru.myitschool.work.api.admin.ApiServiceAdmin
import ru.myitschool.work.api.list.ListInfo
import ru.myitschool.work.api.main.ApiServiceMain
import ru.myitschool.work.api.main.Authorities
import ru.myitschool.work.api.main.UserInfo
import ru.myitschool.work.ui.list.ListRepository
import javax.inject.Inject
@HiltViewModel
class AdminViewModel @Inject constructor(
private val apiService: ApiServiceAdmin,
private val apiServiceData: ApiServiceMain,
private val listRepository: ListRepository
) : ViewModel() {
private val _state = MutableStateFlow<ResultState>(ResultState.Initial)
val state = _state.asStateFlow()
private val _stateData = MutableStateFlow<ResultStateData>(ResultStateData.Initial)
val stateData = _stateData.asStateFlow()
fun block(login: String, authority: Boolean) {
viewModelScope.launch {
_state.value = ResultState.Loading
try {
val response = apiService.block(
login = login,
data = AdminJson(
if (authority) Authorities.ROLE_BLOCK
else Authorities.ROLE_USER
)
)
when (response.code()) {
200 -> {
_state.value = ResultState.Success
}
403 -> {
_state.value = ResultState.InvalidCredentials
}
401 -> {
_state.value = ResultState.Error
}
else -> {
_state.value = ResultState.Error
}
}
} catch (t: Throwable) {
_state.value = ResultState.Error
}
}
}
fun getUserData(login: String) {
viewModelScope.launch {
_stateData.value = ResultStateData.Loading
apiServiceData.getDataUser(login).enqueue(object : Callback<UserInfo> {
override fun onResponse(call: Call<UserInfo>, response: Response<UserInfo>) {
when {
response.isSuccessful -> {
val userInfo = response.body()
_stateData.value = userInfo?.let { ResultStateData.Success(it) }!!
}
response.code() == 401 -> {
_stateData.value = ResultStateData.Error("Unauthorized")
}
response.code() == 403 -> {
_stateData.value = ResultStateData.Error("Block")
}
else -> {
_stateData.value = ResultStateData.Error("Error")
}
}
}
override fun onFailure(call: Call<UserInfo>, t: Throwable) {
_stateData.value = ResultStateData.Error("Network Error")
}
})
}
}
private val _listState = MutableStateFlow<ListState>(ListState.Loading)
val listState: StateFlow<ListState> = _listState
sealed class ListState {
data object Loading : ListState()
data class Success(val data: List<ListInfo>) : ListState()
data class Error(val message: String) : ListState()
}
fun loadList(login: String) {
viewModelScope.launch {
_listState.value = ListState.Loading
try {
val list = listRepository.getList(login)
_listState.value = ListState.Success(list)
} catch (e: Exception) {
_listState.value = ListState.Error(e.message ?: "Unknown error")
}
}
}
sealed class ResultState {
data object Initial : ResultState()
data object InvalidCredentials : ResultState()
data object Loading : ResultState()
data object Success : ResultState()
data object Error : ResultState()
}
sealed class ResultStateData {
data object Initial : ResultStateData()
data object Loading : ResultStateData()
data class Success(val userInfo: UserInfo) : ResultStateData()
data class Error(val message: String) : ResultStateData()
}
}

View File

@ -9,7 +9,7 @@ class ListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val binding = ItemRecyclerViewEntrancedataBinding.bind(itemView)
fun bind(item: ListInfo) {
binding.text1.text = item.time
binding.text1.text = item.time.replace("T", " ")
binding.text2.text = item.value
binding.text3.text = item.type
}

View File

@ -103,8 +103,6 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
text = "Ошибка авторизации"
}
authPreferences.clearLoginState()
Log.d("Authentication", "Ошибка авторизации")
}

View File

@ -36,7 +36,7 @@ class LoginViewModel @Inject constructor(
_state.value = LoginState.InvalidCredentials
}
401 -> {
_state.value = LoginState.Error
_state.value = LoginState.InvalidCredentials
}
else -> {
_state.value = LoginState.Error

View File

@ -8,10 +8,11 @@ import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.squareup.picasso.Picasso
import dagger.hilt.android.AndroidEntryPoint
import ru.myitschool.work.R
import ru.myitschool.work.api.main.Authorities
import ru.myitschool.work.databinding.FragmentMainBinding
import ru.myitschool.work.ui.admin.AdminDestination
import ru.myitschool.work.ui.list.ListAdapter
import ru.myitschool.work.ui.login.LoginDestination
import ru.myitschool.work.ui.scan.qr.QrScanDestination
@ -100,6 +101,16 @@ class MainFragment : Fragment(R.layout.fragment_main) {
username.text = state.userInfo.name
position.text = state.userInfo.position
if (state.userInfo.authority[0].authority == Authorities.ROLE_ADMIN) {
admin.visibleOrGone(true)
} else if (state.userInfo.authority[0].authority == Authorities.ROLE_USER) {
admin.visibleOrGone(false)
} else {
admin.visibleOrGone(false)
Log.e("MainFragment", "Unknown role")
}
loadImageFromUrl(state.userInfo.photoUrl)
val inputFormat = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault())
@ -107,7 +118,8 @@ class MainFragment : Fragment(R.layout.fragment_main) {
try {
val parsedDate = inputFormat.parse(state.userInfo.lastVisit)
lastEntry.text = parsedDate?.let { outputFormat.format(it) } ?: state.userInfo.lastVisit
lastEntry.text = parsedDate?.let { outputFormat.format(it) }
?: state.userInfo.lastVisit
} catch (e: Exception) {
lastEntry.text = state.userInfo.lastVisit
}
@ -125,13 +137,13 @@ class MainFragment : Fragment(R.layout.fragment_main) {
viewModel.listState.collectWhenStarted(this) { state ->
when (state) {
is MainViewModel.ListState.Loading -> {
//TODO
hideAll()
}
is MainViewModel.ListState.Success -> {
listAdapter.submitList(state.data)
}
is MainViewModel.ListState.Error -> {
//TODO
hideAll()
binding.error.text = state.message
}
}
@ -139,11 +151,7 @@ class MainFragment : Fragment(R.layout.fragment_main) {
}
private fun loadImageFromUrl(url: String) {
Picasso
.get()
.load(url)
.error(R.drawable.icon_profile)
.into(binding.photo)
}
private fun setupMainComponents() {
@ -152,6 +160,10 @@ class MainFragment : Fragment(R.layout.fragment_main) {
navigateToLogin()
}
binding.admin.setOnClickListener {
navigateToAdmin()
}
binding.refresh.setOnClickListener {
viewModel.getUserData(authPreferences.getLogin() ?: "")
}
@ -173,6 +185,19 @@ class MainFragment : Fragment(R.layout.fragment_main) {
}
}
private fun navigateToAdmin() {
try {
authPreferences.clearLoginState()
findNavController().apply {
popBackStack(AdminDestination, false)
navigate(AdminDestination)
}
} catch (e: Exception) {
Log.e("MainFragment", "Error navigating to login", e)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View File

@ -36,12 +36,15 @@ class MainViewModel @Inject constructor(
when {
response.isSuccessful -> {
val userInfo = response.body() ?: UserInfo.Initial
_state.value = MainState.Success(userInfo)
val userInfo = response.body()
_state.value = userInfo?.let { MainState.Success(it) }!!
}
response.code() == 401 -> {
_state.value = MainState.Error("Unauthorized")
}
response.code() == 403 -> {
_state.value = MainState.Error("Block")
}
else -> {
_state.value = MainState.Error("Error")
}

View File

@ -58,7 +58,9 @@ class ResultFragment : Fragment(R.layout.fragment_scan_result) {
if (qrData != null) {
authPreferences.getLogin()?.let { login ->
val currentTime = getCurrentTime()
viewModel.open(login, CodeJson(
value = qrData.toString(),
type = OpenType.QR_TYPE,

View File

@ -1,6 +0,0 @@
package ru.myitschool.work.ui.result
object TextStatus {
const val error = "Вход был отменён/Operation was cancelled"
const val success = "Успешно/Success"
}

View File

@ -22,7 +22,6 @@ import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentQrScanBinding
import ru.myitschool.work.ui.main.MainDestination
import ru.myitschool.work.ui.result.ResultDestination
import ru.myitschool.work.utils.QrPreferences
import ru.myitschool.work.utils.collectWhenStarted
@ -41,10 +40,8 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
private val viewModel: QrScanViewModel by viewModels()
private lateinit var qrPreferences: QrPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
qrPreferences = QrPreferences(requireContext())
}
@ -77,8 +74,7 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
goBack()
}
is QrScanViewModel.Action.CloseWithResult -> {
sendResult(QrScanDestination.packToBundle(action.result))
goResult()
// Удаляем этот блок, так как мы будем обрабатывать результат в другом месте
}
}
}
@ -108,8 +104,12 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
executor
) { result ->
result?.getValue(barcodeScanner)?.firstOrNull()?.let { value ->
viewModel.findBarcode(value)
qrPreferences.saveQr(value.displayValue ?: value.rawValue ?: "")
sendResult(QrScanDestination.packToBundle(value.displayValue ?: value.rawValue ?: ""))
goResult()
}
}
)
@ -125,31 +125,17 @@ class QrScanFragment : Fragment(R.layout.fragment_qr_scan) {
super.onDestroyView()
}
private fun goBack() {
val qrData = arguments?.let { QrScanDestination.getDataIfExist(it) }
qrPreferences.saveQr(qrData.toString())
findNavControllerOrNull()?.popBackStack()
?: requireActivity().onBackPressedDispatcher.onBackPressed()
}
private fun goResult() {
try {
val qrData = arguments?.let { QrScanDestination.getDataIfExist(it) }
qrPreferences.saveQr(qrData.toString())
Log.i("ResultFragment", qrData.toString())
findNavController().apply {
popBackStack(ResultDestination, false)
navigate(ResultDestination)
}
} catch (e: Exception) {
Log.e("ResultFragment", "Navigation error", e)
Toast.makeText(context, getText(R.string.errorGoText).toString(), Toast.LENGTH_SHORT).show()
findNavController().apply {
popBackStack(ResultDestination, false)
navigate(ResultDestination)
}
}
private fun goBack() {
findNavControllerOrNull()?.popBackStack()
?: requireActivity().onBackPressedDispatcher.onBackPressed()
}
private fun sendResult(bundle: Bundle) {
setFragmentResult(

View File

@ -0,0 +1,7 @@
<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">
<path android:fillColor="@android:color/white" android:pathData="M17,11c0.34,0 0.67,0.04 1,0.09V6.27L10.5,3L3,6.27v4.91c0,4.54 3.2,8.79 7.5,9.82c0.55,-0.13 1.08,-0.32 1.6,-0.55C11.41,19.47 11,18.28 11,17C11,13.69 13.69,11 17,11z"/>
<path android:fillColor="@android:color/white" android:pathData="M17,13c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4C21,14.79 19.21,13 17,13zM17,14.38c0.62,0 1.12,0.51 1.12,1.12s-0.51,1.12 -1.12,1.12s-1.12,-0.51 -1.12,-1.12S16.38,14.38 17,14.38zM17,19.75c-0.93,0 -1.74,-0.46 -2.24,-1.17c0.05,-0.72 1.51,-1.08 2.24,-1.08s2.19,0.36 2.24,1.08C18.74,19.29 17.93,19.75 17,19.75z"/>
</vector>

View File

@ -2,7 +2,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<stroke
android:width="3dp"
android:width="2dp"
android:color="@color/beige" />
<corners android:radius="8dp"/>
</shape>

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="169dp"
android:height="169dp"
android:viewportWidth="169"
android:viewportHeight="169">
android:width="77.53dp"
android:height="115.35dp"
android:viewportWidth="77.53"
android:viewportHeight="115.35">
<path
android:pathData="M55.3,3.4c-15.3,5 -24.2,16.8 -25.1,33.2 -0.2,4.4 0.1,10.4 0.7,13.3 1.3,6.3 5.3,15 8.7,19l2.3,2.8 -7.2,3.6c-15.7,7.8 -27.3,22.3 -32.2,40.6 -0.9,3.1 -1.8,10.6 -2.2,16.8 -0.6,11 -0.6,11.2 2,13.5 4.1,3.9 19.6,11.5 30.7,15.2 13.9,4.6 24.4,6 39.4,5.3 13.5,-0.6 23,-2.8 36.5,-8.2 8.3,-3.3 9,-3.4 14.7,-2.4 11.3,2 24.8,-2.5 33.4,-11.1 19.5,-19.5 14,-52 -10.7,-64.2 -8.9,-4.4 -20.1,-5.1 -30.1,-1.9l-6.8,2.2 -5.9,-3.6c-3.3,-2 -7,-3.8 -8.2,-4.1 -2.9,-0.8 -2.9,-1 0.7,-6.4 8.7,-13.1 10.8,-31 5.1,-44.4 -3,-6.8 -10.1,-13.9 -17.4,-17.2 -5,-2.3 -7.9,-2.8 -15.2,-3.1 -5.7,-0.2 -10.5,0.2 -13.2,1.1zM78.1,11.5c9.4,3.2 16.1,11.6 18,22.2 3.4,19.6 -12.9,44.3 -29.2,44.3 -9,-0 -19.9,-9.3 -25.7,-22 -2.9,-6.5 -4.2,-20.5 -2.3,-26.2 3,-9.1 10.1,-16.3 18.2,-18.7 5.3,-1.5 16.1,-1.3 21,0.4zM57.2,84c8,3.6 18.2,2.1 26.6,-3.9 2.7,-1.9 2.8,-1.9 7.7,0.3 2.8,1.2 6.5,3 8.4,4l3.3,1.8 -3.4,3.6c-4.9,5.2 -7.5,9.9 -9.3,16.7 -3.9,15 0.9,30.1 12.8,40.7l4.2,3.7 -3,1.1c-14.4,5.5 -19.6,6.4 -37.5,6.4 -16.1,-0 -18.2,-0.2 -26.5,-2.7 -4.9,-1.5 -14.2,-5.3 -20.5,-8.4l-11.5,-5.8 -0.3,-6.6c-1,-22.2 11.3,-43.2 30.9,-52.8l8.3,-4 2.7,2c1.5,1.1 4.7,2.8 7.1,3.9zM142.5,87.8c6.8,3.3 12.7,9.2 15.9,16.1 3.2,7.1 3,19.9 -0.6,26.6 -3.2,6.2 -10.4,13 -16.6,15.8 -6.5,2.9 -17.9,2.9 -24.4,-0 -6.6,-3 -14.1,-10.4 -16.9,-16.6 -3.2,-7.1 -3.3,-19.1 -0.2,-25.8 3,-6.7 8.6,-12.4 15.5,-15.9 5.2,-2.5 7.3,-3 13.7,-3 6.3,-0 8.6,0.5 13.6,2.8z"
android:fillColor="#000000"
android:strokeColor="#00000000"/>
android:pathData="M28.08,43.07h3.94v1h-3.94z"
android:fillColor="#E9D4C3"/>
<path
android:pathData="M134.5,112l-9.9,9.9 -6.2,-5.9c-3.4,-3.3 -6.7,-6 -7.2,-6 -1.4,-0 -4.2,2.9 -4.2,4.3 0,0.7 3.8,5 8.5,9.7 8.2,8.1 8.5,8.3 11,7 3.4,-1.8 24.5,-23.5 24.5,-25.2 0,-2 -2.1,-3.8 -4.5,-3.8 -1.3,-0 -5.8,3.8 -12,10z"
android:fillColor="#000000"
android:strokeColor="#00000000"/>
android:pathData="M36.1,43.07h12.85v1h-12.85z"
android:fillColor="#E9D4C3"/>
<path
android:pathData="M72.47,26l-2.49,28.5a5.18,5.18 0,0 1,-5.06 4.65,4.2 4.2,0 0,1 -4.25,-4.65L63.14,26a15.08,15.08 0,0 0,-15.24 -16.7L37.74,9.3A18.61,18.61 0,0 0,19.58 26l-1,11.64h-9.3l1,-11.64c1.25,-14.34 13.94,-26 28.27,-26h10.2A23.47,23.47 0,0 1,72.47 26Z"
android:fillColor="#E9D4C3"/>
<path
android:pathData="M72.47,26l-2.49,28.5a5.18,5.18 0,0 1,-5.06 4.65,4.2 4.2,0 0,1 -4.25,-4.65L63.14,26a15.08,15.08 0,0 0,-15.24 -16.7L37.74,9.3A18.61,18.61 0,0 0,19.58 26l-1,11.64h-9.3l1,-11.64c1.25,-14.34 13.94,-26 28.27,-26h10.2A23.47,23.47 0,0 1,72.47 26Z"
android:strokeAlpha="0.5"
android:fillColor="#fff"
android:fillAlpha="0.5"/>
<path
android:pathData="M73.14,53.46 L68.29,109a7.07,7.07 0,0 1,-6.92 6.35L5.83,115.35a5.73,5.73 0,0 1,-5.8 -6.35l4.86,-55.54a7.07,7.07 0,0 1,3.46 -5.39,6.8 6.8,0 0,1 3.46,-1L67.34,47.07a6,6 0,0 1,3.21 0.91A5.76,5.76 0,0 1,73.14 53.46Z"
android:fillColor="#E9D4C3"/>
<path
android:pathData="M15.39,77.72l14.43,22.21 47.59,-47.59a0.48,0.48 0,0 0,-0.63 -0.71l-45.35,35.55 -8.57,-13.81Z"
android:fillColor="#fff"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M17.5,12C20.538,12 23,14.462 23,17.5C23,20.537 20.538,23 17.5,23C14.463,23 12,20.537 12,17.5C12,14.462 14.463,12 17.5,12ZM14.854,17.146C14.658,16.951 14.342,16.951 14.147,17.146C13.951,17.341 13.951,17.658 14.147,17.853L16.147,19.853C16.342,20.049 16.658,20.049 16.854,19.853L20.854,15.853C21.049,15.658 21.049,15.342 20.854,15.146C20.658,14.951 20.342,14.951 20.147,15.146L16.5,18.793L14.854,17.146ZM12.022,13.999C11.726,14.462 11.486,14.966 11.314,15.499L4.253,15.5C3.839,15.5 3.504,15.835 3.504,16.249V16.826C3.504,17.362 3.695,17.88 4.043,18.287C5.296,19.755 7.262,20.501 10,20.501C10.597,20.501 11.156,20.465 11.68,20.395C11.925,20.89 12.233,21.348 12.592,21.761C11.796,21.921 10.932,22.001 10,22.001C6.854,22.001 4.468,21.096 2.902,19.261C2.322,18.583 2.004,17.719 2.004,16.826V16.249C2.004,15.007 3.011,14 4.253,14L12.022,13.999ZM10,2.004C12.762,2.004 15,4.243 15,7.004C15,9.766 12.762,12.004 10,12.004C7.239,12.004 5,9.766 5,7.004C5,4.243 7.239,2.004 10,2.004ZM10,3.504C8.067,3.504 6.5,5.071 6.5,7.004C6.5,8.937 8.067,10.504 10,10.504C11.933,10.504 13.5,8.937 13.5,7.004C13.5,5.071 11.933,3.504 10,3.504Z"
android:fillColor="#212121"/>
</vector>

View File

@ -19,7 +19,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.23" />
app:layout_constraintGuide_percent="0.21" />
<TextView
android:id="@+id/error"
@ -39,7 +39,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.65" />
app:layout_constraintGuide_percent="0.62" />
<View
android:id="@+id/close"
@ -54,8 +54,8 @@
<View
android:id="@+id/success_icon"
android:layout_width="230dp"
android:layout_height="230dp"
android:layout_width="190dp"
android:layout_height="250dp"
android:background="@drawable/res_success"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/button_beigestroke_whitefill"
android:background="@drawable/button_grestroke_whiteback"
android:layout_width="320dp"
android:layout_height="80dp"
android:orientation="vertical"

View File

@ -1,5 +1,5 @@
<resources>
<string name="app_name">NTO Pass</string>
<string name="app_name">DoorRock</string>
<string name="loginText">логин</string>
<string name="welcomeText">Добро пожаловать!</string>
<string name="inputLoginText">Введите свой логин</string>