Compare commits

...

6 Commits
v1.0 ... master

Author SHA1 Message Date
a1pha
7a896a793f Merge remote-tracking branch 'origin/master' 2025-02-20 18:19:11 +03:00
a1pha
aba81f744f fix: paging fixed 2025-02-20 18:18:57 +03:00
46616380aa Merge remote-tracking branch 'origin/master' 2025-02-20 20:14:54 +05:00
b5d88d361e from muschinkina_maria_3 2025-02-20 20:14:29 +05:00
a1pha
c3adf16666 feat: last commit 2025-02-20 17:05:01 +03:00
a1pha
5297d47340 feat: last commit 2025-02-20 16:54:50 +03:00
16 changed files with 180 additions and 89 deletions

View File

@ -71,3 +71,5 @@
- Если сервер ответил любой ошибкой - то отображаем текст: - Если сервер ответил любой ошибкой - то отображаем текст:
*"Что-то пошло не так/Something wrong"* *"Что-то пошло не так/Something wrong"*
- Кнопка закрытия всегда открывает главный экран. - Кнопка закрытия всегда открывает главный экран.
### Ссылка на макеты в фигме: https://www.figma.com/design/MoPjxN7VnWnor3Mq0CNB8s/%D0%9D%D0%A2%D0%9E?node-id=49-193&t=FTPnWNwArVdAGM33-1

View File

@ -1,5 +1,6 @@
package ru.myitschool.work.data package ru.myitschool.work.data
import android.util.Log
import ru.myitschool.work.data.dto.PassDto import ru.myitschool.work.data.dto.PassDto
import ru.myitschool.work.data.local.CredentialsLocalDataSource import ru.myitschool.work.data.local.CredentialsLocalDataSource
import ru.myitschool.work.data.network.PassNetworkDataSource import ru.myitschool.work.data.network.PassNetworkDataSource

View File

@ -77,7 +77,7 @@ class UserRepositoryImpl(
override suspend fun getUserByLogin(login: String): Result<UserEntity> { override suspend fun getUserByLogin(login: String): Result<UserEntity> {
return networkDataSource.getUserByLogin(login, credentialsLocalDataSource.getToken()).fold( return networkDataSource.getUserByLogin(login, credentialsLocalDataSource.getToken()).fold(
onSuccess = { map(it) }, onFailure = { error(it) } onSuccess = { map(it) }, onFailure = { Result.failure(it) }
) )
} }
} }

View File

@ -6,9 +6,9 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class PassDto( data class PassDto(
@SerialName("time")
val time: String?,
@SerialName("localDateTime") @SerialName("localDateTime")
val time: String?,
@SerialName("terminal")
val terminal: TerminalDto? val terminal: TerminalDto?
) )

View File

@ -15,7 +15,7 @@ object AdminNetworkDataSource {
suspend fun blockUser(login: String, token: String): Result<Unit> = suspend fun blockUser(login: String, token: String): Result<Unit> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
runCatching { runCatching {
val response = client.patch("$SERVER_ADDRESS/api/employees/block?login=$login") { val response = client.patch("$SERVER_ADDRESS/api/users/block?username=$login") {
headers { headers {
append(HttpHeaders.Authorization, token) append(HttpHeaders.Authorization, token)
} }
@ -30,7 +30,7 @@ object AdminNetworkDataSource {
suspend fun unblockUser(login: String, token: String): Result<Unit> = suspend fun unblockUser(login: String, token: String): Result<Unit> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
runCatching { runCatching {
val response = client.patch("$SERVER_ADDRESS/api/employees/unblock?login=$login") { val response = client.patch("$SERVER_ADDRESS/api/users/unblock?username=$login") {
headers { headers {
append(HttpHeaders.Authorization, token) append(HttpHeaders.Authorization, token)
} }

View File

@ -52,13 +52,13 @@ object UserNetworkDataSource {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
runCatching { runCatching {
val response = val response =
KtorClient.client.get("${Constants.SERVER_ADDRESS}/api/employees/find?${login}") { KtorClient.client.get("${Constants.SERVER_ADDRESS}/api/users/get?username=${login}") {
headers { headers {
append(HttpHeaders.Authorization, token) append(HttpHeaders.Authorization, token)
} }
} }
if (response.status == HttpStatusCode.OK) if (response.status != HttpStatusCode.OK)
error("Status ${response.status}") error("Status ${response.status}")
response.body() response.body()
} }

View File

@ -4,6 +4,7 @@ import android.os.Bundle
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 ru.myitschool.work.R import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentFindEmployeeBinding import ru.myitschool.work.databinding.FragmentFindEmployeeBinding
import ru.myitschool.work.utils.collectWithLifecycle import ru.myitschool.work.utils.collectWithLifecycle
@ -23,7 +24,7 @@ class AdminFragment : Fragment(R.layout.fragment_find_employee) {
when (state) { when (state) {
is AdminViewModel.State.Error -> binding.error.text = state.errorMessage is AdminViewModel.State.Error -> binding.error.text = state.errorMessage
is AdminViewModel.State.Loading -> Unit is AdminViewModel.State.Loading -> Unit
is AdminViewModel.State.Show -> {} is AdminViewModel.State.Show -> findNavController().navigate(R.id.action_adminFragment_to_viewUserAsAdminFragment)
is AdminViewModel.State.Waiting -> Unit is AdminViewModel.State.Waiting -> Unit
} }
} }
@ -31,7 +32,6 @@ class AdminFragment : Fragment(R.layout.fragment_find_employee) {
binding.find.setOnClickListener { binding.find.setOnClickListener {
viewModel.onFind(binding.username.text.toString()) viewModel.onFind(binding.username.text.toString())
} }
} }
override fun onDestroy() { override fun onDestroy() {

View File

@ -1,14 +1,14 @@
package ru.myitschool.work.ui.admin.search package ru.myitschool.work.ui.admin.search
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.CreationExtras
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn import androidx.paging.cachedIn
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -38,17 +38,18 @@ class AdminViewModel(
private val _state = MutableStateFlow<State>(State.Waiting) private val _state = MutableStateFlow<State>(State.Waiting)
val state = _state.asStateFlow() val state = _state.asStateFlow()
private var _listState: Flow<PagingData<PassEntity>>? = null private val _listState = MutableLiveData<Pager<Int, PassEntity>>()
val listState: Flow<PagingData<PassEntity>> get() = _listState!! val listState = _listState.value?.flow?.cachedIn(viewModelScope)
private var currentLogin: String? = null private val _currentLogin = MutableLiveData<String>()
private val currentLogin: LiveData<String> get() = _currentLogin
fun onFind(login: String) { fun onFind(login: String) {
viewModelScope.launch { viewModelScope.launch {
_state.emit(State.Loading) _state.emit(State.Loading)
getUserByLoginUseCase(login).fold( getUserByLoginUseCase(login).fold(
onSuccess = { data -> onSuccess = { data ->
currentLogin = login _currentLogin.postValue(login)
_state.emit(State.Show(data)) _state.emit(State.Show(data))
setUpPager(login) setUpPager(login)
}, },
@ -64,7 +65,7 @@ class AdminViewModel(
private fun updateState() { private fun updateState() {
viewModelScope.launch { viewModelScope.launch {
_state.emit(State.Loading) _state.emit(State.Loading)
getUserByLoginUseCase(currentLogin!!).fold( getUserByLoginUseCase(currentLogin.value!!).fold(
onSuccess = { _state.emit(State.Show(it)) }, onSuccess = { _state.emit(State.Show(it)) },
onFailure = { _state.emit(State.Error(it.message.toString())) } onFailure = { _state.emit(State.Error(it.message.toString())) }
) )
@ -72,7 +73,7 @@ class AdminViewModel(
} }
private fun setUpPager(login: String) { private fun setUpPager(login: String) {
_listState = Pager( _listState.value = Pager(
config = PagingConfig( config = PagingConfig(
pageSize = 10, pageSize = 10,
enablePlaceholders = false, enablePlaceholders = false,
@ -80,19 +81,18 @@ class AdminViewModel(
) )
) { ) {
UsersPassesPagingSource(getUsersPassesUseCase::invoke, login) UsersPassesPagingSource(getUsersPassesUseCase::invoke, login)
}.flow }
.cachedIn(viewModelScope)
} }
fun onBlock() { fun onBlock() {
viewModelScope.launch { viewModelScope.launch {
blockUserUseCase(currentLogin!!) blockUserUseCase(currentLogin.value!!)
} }
} }
fun unblock() { fun unblock() {
viewModelScope.launch { viewModelScope.launch {
unBlockUserUseCase(currentLogin!!) unBlockUserUseCase(currentLogin.value!!)
} }
} }

View File

@ -49,27 +49,28 @@ class ViewUserAsAdminFragment : Fragment(R.layout.fragment_user) {
is AdminViewModel.State.Error -> binding.error.text = state.errorMessage is AdminViewModel.State.Error -> binding.error.text = state.errorMessage
is AdminViewModel.State.Waiting -> Unit is AdminViewModel.State.Waiting -> Unit
} }
}
viewModel.listState.collectWithLifecycle(this) { listState -> viewModel.listState?.collectWithLifecycle(this) { listState ->
adapter.submitData(listState) adapter.submitData(listState)
} }
adapter.loadStateFlow.collectWithLifecycle(this) { data -> adapter.loadStateFlow.collectWithLifecycle(this) { data ->
val dataState = data.refresh val dataState = data.refresh
binding.refresh.isRefreshing = dataState is LoadState.Loading binding.refresh.isRefreshing = dataState is LoadState.Loading
binding.error.visibleOrGone(dataState is LoadState.Error) binding.error.visibleOrGone(dataState is LoadState.Error)
if (dataState is LoadState.Error) { if (dataState is LoadState.Error) {
binding.error.text = dataState.error.toString() binding.error.text = dataState.error.toString()
}
}
binding.refresh.setOnRefreshListener {
viewModel.onRefresh()
adapter.refresh()
} }
} }
binding.refresh.setOnRefreshListener {
viewModel.onRefresh()
adapter.refresh()
}
binding.block.setOnClickListener { binding.block.setOnClickListener {
AlertDialog.Builder(requireContext()) AlertDialog.Builder(requireContext())
.setTitle("Блокировка доступа") .setTitle("Блокировка доступа")

View File

@ -8,7 +8,7 @@ import androidx.recyclerview.widget.RecyclerView
import ru.myitschool.work.databinding.PassItemBinding import ru.myitschool.work.databinding.PassItemBinding
import ru.myitschool.work.domain.entities.PassEntity import ru.myitschool.work.domain.entities.PassEntity
class PassesListAdapter: class PassesListAdapter :
PagingDataAdapter<PassEntity, PassesListAdapter.ViewHolder>(CenterDiff) { PagingDataAdapter<PassEntity, PassesListAdapter.ViewHolder>(CenterDiff) {
class ViewHolder( class ViewHolder(
@ -40,9 +40,9 @@ class PassesListAdapter:
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind( holder.bind(
getItem(position) ?: PassEntity( getItem(position) ?: PassEntity(
type = "Загрузка ...", type = "Loading...",
name = "Терминал №...", name = "Loading...",
time = "Давным-давно..." time = "Loading..."
) )
) )
} }

View File

@ -1,5 +1,6 @@
package ru.myitschool.work.ui.profile package ru.myitschool.work.ui.profile
import android.util.Log
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.paging.PagingState import androidx.paging.PagingState
import ru.myitschool.work.domain.entities.PassEntity import ru.myitschool.work.domain.entities.PassEntity
@ -15,12 +16,12 @@ class PassesPagingSource(
} }
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, PassEntity> { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, PassEntity> {
val pageNum = params.key ?: 1 val pageNum = params.key ?: 0
return request.invoke(pageNum, params.loadSize).fold( return request.invoke(pageNum, params.loadSize).fold(
onSuccess = { value -> onSuccess = { value ->
LoadResult.Page( LoadResult.Page(
data = value, data = value,
prevKey = (pageNum - 1).takeIf { it >= 0 }, prevKey = (pageNum - 1).takeIf { it > 0 },
nextKey = (pageNum + 1).takeIf { value.size == params.loadSize } nextKey = (pageNum + 1).takeIf { value.size == params.loadSize }
) )
}, },

View File

@ -33,7 +33,6 @@ class UserFragment : Fragment(R.layout.fragment_user) {
viewModel.state.collectWithLifecycle(this) { state -> viewModel.state.collectWithLifecycle(this) { state ->
binding.refresh.isRefreshing = state is UserViewModel.State.Loading binding.refresh.isRefreshing = state is UserViewModel.State.Loading
binding.content.visibleOrGone(state is UserViewModel.State.Show) binding.content.visibleOrGone(state is UserViewModel.State.Show)
Log.d("info", state.toString())
when (state) { when (state) {
is UserViewModel.State.Loading -> Unit is UserViewModel.State.Loading -> Unit
is UserViewModel.State.Show -> { is UserViewModel.State.Show -> {
@ -46,53 +45,55 @@ class UserFragment : Fragment(R.layout.fragment_user) {
Picasso.get().load(user.photoUrl).into(binding.photo) Picasso.get().load(user.photoUrl).into(binding.photo)
} }
} }
}
viewModel.listState.collectWithLifecycle(this) { listState -> viewModel.listState.collectWithLifecycle(this) { listState ->
adapter.submitData(listState) adapter.submitData(listState)
} }
adapter.loadStateFlow.collectWithLifecycle(this) { data -> adapter.loadStateFlow.collectWithLifecycle(this) { data ->
val dataState = data.refresh Log.d("info", data.refresh.toString())
binding.refresh.isRefreshing = dataState is LoadState.Loading val dataState = data.refresh
binding.error.visibleOrGone(dataState is LoadState.Error) binding.refresh.isRefreshing = dataState is LoadState.Loading
binding.error.visibleOrGone(dataState is LoadState.Error)
if (dataState is LoadState.Error) { if (dataState is LoadState.Error) {
binding.error.text = dataState.error.toString() binding.error.text = dataState.error.toString()
}
}
binding.refresh.setOnRefreshListener {
viewModel.onRefresh()
adapter.refresh()
}
binding.logout.setOnClickListener {
AlertDialog.Builder(requireContext())
.setTitle("Выход")
.setMessage("Вы уверены, что хотите выйти из аккаунта?")
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok) { _, _ ->
viewModel.onLogout()
findNavController().navigate(R.id.action_userFragment_to_loginFragment)
}
.show()
}
binding.findUser.setOnClickListener {
findNavController().navigate(R.id.find_user)
}
binding.scan.setOnClickListener {
findNavController().navigate(R.id.action_userFragment_to_qrScanFragment)
}
parentFragmentManager.setFragmentResultListener(
QrScanDestination.REQUEST_KEY, this
) { _, result ->
parentFragmentManager.setFragmentResult(RESPONSE_KEY, result)
findNavController().navigate(R.id.action_userFragment_to_qrResultFragment)
} }
} }
binding.refresh.setOnRefreshListener {
viewModel.onRefresh()
adapter.refresh()
}
binding.logout.setOnClickListener {
AlertDialog.Builder(requireContext())
.setTitle("Выход")
.setMessage("Вы уверены, что хотите выйти из аккаунта?")
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok) { _, _ ->
viewModel.onLogout()
findNavController().navigate(R.id.action_userFragment_to_loginFragment)
}
.show()
}
binding.findUser.setOnClickListener {
findNavController().navigate(R.id.action_userFragment_to_adminFragment)
}
binding.scan.setOnClickListener {
findNavController().navigate(R.id.action_userFragment_to_qrScanFragment)
}
parentFragmentManager.setFragmentResultListener(
QrScanDestination.REQUEST_KEY, this
) { _, result ->
parentFragmentManager.setFragmentResult(RESPONSE_KEY, result)
findNavController().navigate(R.id.action_userFragment_to_qrResultFragment)
}
} }
override fun onDestroy() { override fun onDestroy() {

View File

@ -1,5 +1,6 @@
package ru.myitschool.work.ui.profile package ru.myitschool.work.ui.profile
import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@ -16,6 +17,7 @@ import ru.myitschool.work.data.local.CredentialsLocalDataSource
import ru.myitschool.work.data.local.UserLocalDataSource import ru.myitschool.work.data.local.UserLocalDataSource
import ru.myitschool.work.data.network.PassNetworkDataSource import ru.myitschool.work.data.network.PassNetworkDataSource
import ru.myitschool.work.data.network.UserNetworkDataSource import ru.myitschool.work.data.network.UserNetworkDataSource
import ru.myitschool.work.domain.entities.PassEntity
import ru.myitschool.work.domain.entities.UserEntity import ru.myitschool.work.domain.entities.UserEntity
import ru.myitschool.work.domain.login.LogoutUseCase import ru.myitschool.work.domain.login.LogoutUseCase
import ru.myitschool.work.domain.passes.GetCurrentPassesUseCase import ru.myitschool.work.domain.passes.GetCurrentPassesUseCase
@ -48,7 +50,14 @@ class UserViewModel(
private fun updateState() { private fun updateState() {
viewModelScope.launch { viewModelScope.launch {
_state.emit(State.Loading) _state.emit(State.Loading)
_state.emit(State.Show(getCurrentUserUseCase()))
_state.emit(
State.Show(
getCurrentUserUseCase(),
getCurrentPassesUseCase(0, 30).getOrNull()!!
)
)
} }
} }
@ -62,7 +71,7 @@ class UserViewModel(
sealed interface State { sealed interface State {
data object Loading : State data object Loading : State
data class Show(val userEntity: UserEntity) : State data class Show(val userEntity: UserEntity, val passes: List<PassEntity>) : State
} }
companion object { companion object {

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
android:height="108dp"
android:width="108dp"
android:viewportHeight="108"
android:viewportWidth="108"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>

View File

@ -160,6 +160,7 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/passes" android:id="@+id/passes"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" android:layout_marginHorizontal="20dp"

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/> <background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>