Add action for empty list state
This commit is contained in:
@@ -36,13 +36,14 @@ class FavouritesListViewModel(
|
|||||||
when {
|
when {
|
||||||
list.isEmpty() -> listOf(
|
list.isEmpty() -> listOf(
|
||||||
EmptyState(
|
EmptyState(
|
||||||
R.drawable.ic_heart_outline,
|
icon = R.drawable.ic_heart_outline,
|
||||||
R.string.text_empty_holder_primary,
|
textPrimary = R.string.text_empty_holder_primary,
|
||||||
if (categoryId == 0L) {
|
textSecondary = if (categoryId == 0L) {
|
||||||
R.string.you_have_not_favourites_yet
|
R.string.you_have_not_favourites_yet
|
||||||
} else {
|
} else {
|
||||||
R.string.favourites_category_empty
|
R.string.favourites_category_empty
|
||||||
}
|
},
|
||||||
|
actionStringRes = 0,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else -> list.toUi(mode, this)
|
else -> list.toUi(mode, this)
|
||||||
|
|||||||
@@ -45,7 +45,14 @@ class HistoryListViewModel(
|
|||||||
createListModeFlow()
|
createListModeFlow()
|
||||||
) { list, grouped, mode ->
|
) { list, grouped, mode ->
|
||||||
when {
|
when {
|
||||||
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_history, R.string.text_history_holder_primary, R.string.text_history_holder_secondary))
|
list.isEmpty() -> listOf(
|
||||||
|
EmptyState(
|
||||||
|
icon = R.drawable.ic_history,
|
||||||
|
textPrimary = R.string.text_history_holder_primary,
|
||||||
|
textSecondary = R.string.text_history_holder_secondary,
|
||||||
|
actionStringRes = 0,
|
||||||
|
)
|
||||||
|
)
|
||||||
else -> mapList(list, grouped, mode)
|
else -> mapList(list, grouped, mode)
|
||||||
}
|
}
|
||||||
}.onFirst {
|
}.onFirst {
|
||||||
|
|||||||
@@ -15,17 +15,18 @@ import kotlinx.coroutines.launch
|
|||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
|
||||||
import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener
|
import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener
|
||||||
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
|
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
|
||||||
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
|
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareDialog
|
||||||
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
|
||||||
import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
|
import org.koitharu.kotatsu.core.exceptions.resolve.ResolvableException
|
||||||
import org.koitharu.kotatsu.core.model.Manga
|
import org.koitharu.kotatsu.core.model.Manga
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaTag
|
||||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||||
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
||||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
|
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
|
||||||
|
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.main.ui.AppBarOwner
|
import org.koitharu.kotatsu.main.ui.AppBarOwner
|
||||||
import org.koitharu.kotatsu.main.ui.MainActivity
|
import org.koitharu.kotatsu.main.ui.MainActivity
|
||||||
@@ -33,7 +34,7 @@ import org.koitharu.kotatsu.utils.RecycledViewPoolHolder
|
|||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
|
||||||
abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
||||||
PaginationScrollListener.Callback, OnListItemClickListener<Manga>,
|
PaginationScrollListener.Callback, MangaListListener,
|
||||||
SwipeRefreshLayout.OnRefreshListener {
|
SwipeRefreshLayout.OnRefreshListener {
|
||||||
|
|
||||||
private var listAdapter: MangaListAdapter? = null
|
private var listAdapter: MangaListAdapter? = null
|
||||||
@@ -62,10 +63,7 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
|||||||
listAdapter = MangaListAdapter(
|
listAdapter = MangaListAdapter(
|
||||||
coil = get(),
|
coil = get(),
|
||||||
lifecycleOwner = viewLifecycleOwner,
|
lifecycleOwner = viewLifecycleOwner,
|
||||||
clickListener = this,
|
listener = this,
|
||||||
onRetryClick = ::resolveException,
|
|
||||||
onTagRemoveClick = viewModel::onRemoveFilterTag,
|
|
||||||
onFilterClickListener = this::onFilterClick,
|
|
||||||
)
|
)
|
||||||
paginationListener = PaginationScrollListener(4, this)
|
paginationListener = PaginationScrollListener(4, this)
|
||||||
with(binding.recyclerView) {
|
with(binding.recyclerView) {
|
||||||
@@ -192,7 +190,17 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun onFilterClick() = Unit
|
override fun onFilterClick() = Unit
|
||||||
|
|
||||||
|
override fun onEmptyActionClick() = Unit
|
||||||
|
|
||||||
|
override fun onRetryClick(error: Throwable) {
|
||||||
|
resolveException(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTagRemoveClick(tag: MangaTag) {
|
||||||
|
viewModel.onRemoveFilterTag(tag)
|
||||||
|
}
|
||||||
|
|
||||||
private fun onGridScaleChanged(scale: Float) {
|
private fun onGridScaleChanged(scale: Float) {
|
||||||
spanSizeLookup.invalidateCache()
|
spanSizeLookup.invalidateCache()
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
package org.koitharu.kotatsu.list.ui.adapter
|
package org.koitharu.kotatsu.list.ui.adapter
|
||||||
|
|
||||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
||||||
import org.koitharu.kotatsu.core.model.MangaTag
|
import org.koitharu.kotatsu.core.model.MangaTag
|
||||||
import org.koitharu.kotatsu.databinding.ItemCurrentFilterBinding
|
|
||||||
import org.koitharu.kotatsu.list.ui.model.CurrentFilterModel
|
import org.koitharu.kotatsu.list.ui.model.CurrentFilterModel
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
|
|
||||||
fun currentFilterAD(
|
fun currentFilterAD(
|
||||||
onTagRemoveClick: (MangaTag) -> Unit,
|
listener: MangaListListener,
|
||||||
) = adapterDelegateViewBinding<CurrentFilterModel, ListModel, ItemCurrentFilterBinding>(
|
) = adapterDelegate<CurrentFilterModel, ListModel>(R.layout.item_current_filter) {
|
||||||
{ inflater, parent -> ItemCurrentFilterBinding.inflate(inflater, parent, false) }
|
|
||||||
) {
|
|
||||||
|
|
||||||
binding.chipsTags.onChipCloseClickListener = ChipsView.OnChipCloseClickListener { chip, data ->
|
val chipGroup = itemView as ChipsView
|
||||||
onTagRemoveClick(data as? MangaTag ?: return@OnChipCloseClickListener)
|
|
||||||
|
chipGroup.onChipCloseClickListener = ChipsView.OnChipCloseClickListener { chip, data ->
|
||||||
|
listener.onTagRemoveClick(data as? MangaTag ?: return@OnChipCloseClickListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
bind {
|
bind {
|
||||||
binding.chipsTags.setChips(item.chips)
|
chipGroup.setChips(item.chips)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,20 +4,20 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
|||||||
import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding
|
import org.koitharu.kotatsu.databinding.ItemEmptyStateBinding
|
||||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
|
import org.koitharu.kotatsu.utils.ext.setTextAndVisible
|
||||||
|
|
||||||
fun emptyStateListAD() = adapterDelegateViewBinding<EmptyState, ListModel, ItemEmptyStateBinding>(
|
fun emptyStateListAD(
|
||||||
|
listener: MangaListListener,
|
||||||
|
) = adapterDelegateViewBinding<EmptyState, ListModel, ItemEmptyStateBinding>(
|
||||||
{ inflater, parent -> ItemEmptyStateBinding.inflate(inflater, parent, false) }
|
{ inflater, parent -> ItemEmptyStateBinding.inflate(inflater, parent, false) }
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
binding.buttonRetry.setOnClickListener { listener.onEmptyActionClick() }
|
||||||
|
|
||||||
bind {
|
bind {
|
||||||
with(binding.icon) {
|
binding.icon.setImageResource(item.icon)
|
||||||
setImageResource(item.icon)
|
binding.textPrimary.setText(item.textPrimary)
|
||||||
}
|
binding.textSecondary.setTextAndVisible(item.textSecondary)
|
||||||
with(binding.textPrimary) {
|
binding.buttonRetry.setTextAndVisible(item.actionStringRes)
|
||||||
setText(item.textPrimary)
|
|
||||||
}
|
|
||||||
with(binding.textSecondary) {
|
|
||||||
setText(item.textSecondary)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,13 +7,13 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
|
|||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
|
|
||||||
fun errorFooterAD(
|
fun errorFooterAD(
|
||||||
onRetryClick: (Throwable) -> Unit
|
listener: MangaListListener,
|
||||||
) = adapterDelegateViewBinding<ErrorFooter, ListModel, ItemErrorFooterBinding>(
|
) = adapterDelegateViewBinding<ErrorFooter, ListModel, ItemErrorFooterBinding>(
|
||||||
{ inflater, parent -> ItemErrorFooterBinding.inflate(inflater, parent, false) }
|
{ inflater, parent -> ItemErrorFooterBinding.inflate(inflater, parent, false) }
|
||||||
) {
|
) {
|
||||||
|
|
||||||
binding.root.setOnClickListener {
|
binding.root.setOnClickListener {
|
||||||
onRetryClick(item.exception)
|
listener.onRetryClick(item.exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
bind {
|
bind {
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
|
|||||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
|
|
||||||
fun errorStateListAD(
|
fun errorStateListAD(
|
||||||
onRetryClick: (Throwable) -> Unit
|
listener: MangaListListener,
|
||||||
) = adapterDelegateViewBinding<ErrorState, ListModel, ItemErrorStateBinding>(
|
) = adapterDelegateViewBinding<ErrorState, ListModel, ItemErrorStateBinding>(
|
||||||
{ inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) }
|
{ inflater, parent -> ItemErrorStateBinding.inflate(inflater, parent, false) }
|
||||||
) {
|
) {
|
||||||
|
|
||||||
binding.buttonRetry.setOnClickListener {
|
binding.buttonRetry.setOnClickListener {
|
||||||
onRetryClick(item.exception)
|
listener.onRetryClick(item.exception)
|
||||||
}
|
}
|
||||||
|
|
||||||
bind {
|
bind {
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ fun listHeaderAD() = adapterDelegate<ListHeader, ListModel>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun listHeaderWithFilterAD(
|
fun listHeaderWithFilterAD(
|
||||||
onFilterClickListener: () -> Unit,
|
listener: MangaListListener,
|
||||||
) = adapterDelegateViewBinding<ListHeader, ListModel, ItemHeaderWithFilterBinding>(
|
) = adapterDelegateViewBinding<ListHeader, ListModel, ItemHeaderWithFilterBinding>(
|
||||||
viewBinding = { inflater, parent -> ItemHeaderWithFilterBinding.inflate(inflater, parent, false) },
|
viewBinding = { inflater, parent -> ItemHeaderWithFilterBinding.inflate(inflater, parent, false) },
|
||||||
on = { item, _, _ -> item is ListHeader && item.sortOrder != null },
|
on = { item, _, _ -> item is ListHeader && item.sortOrder != null },
|
||||||
) {
|
) {
|
||||||
|
|
||||||
binding.textViewFilter.setOnClickListener {
|
binding.textViewFilter.setOnClickListener {
|
||||||
onFilterClickListener()
|
listener.onFilterClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
bind {
|
bind {
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ import androidx.lifecycle.LifecycleOwner
|
|||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
|
||||||
import org.koitharu.kotatsu.core.model.Manga
|
|
||||||
import org.koitharu.kotatsu.core.model.MangaTag
|
|
||||||
import org.koitharu.kotatsu.core.ui.DateTimeAgo
|
import org.koitharu.kotatsu.core.ui.DateTimeAgo
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
|
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
|
||||||
@@ -17,32 +14,29 @@ import kotlin.jvm.internal.Intrinsics
|
|||||||
class MangaListAdapter(
|
class MangaListAdapter(
|
||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
clickListener: OnListItemClickListener<Manga>,
|
listener: MangaListListener,
|
||||||
onRetryClick: (Throwable) -> Unit,
|
|
||||||
onTagRemoveClick: (MangaTag) -> Unit,
|
|
||||||
onFilterClickListener: () -> Unit,
|
|
||||||
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
|
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
delegatesManager
|
delegatesManager
|
||||||
.addDelegate(
|
.addDelegate(
|
||||||
ITEM_TYPE_MANGA_LIST,
|
ITEM_TYPE_MANGA_LIST,
|
||||||
mangaListItemAD(coil, lifecycleOwner, clickListener)
|
mangaListItemAD(coil, lifecycleOwner, listener)
|
||||||
)
|
)
|
||||||
.addDelegate(
|
.addDelegate(
|
||||||
ITEM_TYPE_MANGA_LIST_DETAILED,
|
ITEM_TYPE_MANGA_LIST_DETAILED,
|
||||||
mangaListDetailedItemAD(coil, lifecycleOwner, clickListener)
|
mangaListDetailedItemAD(coil, lifecycleOwner, listener)
|
||||||
)
|
)
|
||||||
.addDelegate(ITEM_TYPE_MANGA_GRID, mangaGridItemAD(coil, lifecycleOwner, clickListener))
|
.addDelegate(ITEM_TYPE_MANGA_GRID, mangaGridItemAD(coil, lifecycleOwner, listener))
|
||||||
.addDelegate(ITEM_TYPE_LOADING_FOOTER, loadingFooterAD())
|
.addDelegate(ITEM_TYPE_LOADING_FOOTER, loadingFooterAD())
|
||||||
.addDelegate(ITEM_TYPE_LOADING_STATE, loadingStateAD())
|
.addDelegate(ITEM_TYPE_LOADING_STATE, loadingStateAD())
|
||||||
.addDelegate(ITEM_TYPE_DATE, relatedDateItemAD())
|
.addDelegate(ITEM_TYPE_DATE, relatedDateItemAD())
|
||||||
.addDelegate(ITEM_TYPE_ERROR_STATE, errorStateListAD(onRetryClick))
|
.addDelegate(ITEM_TYPE_ERROR_STATE, errorStateListAD(listener))
|
||||||
.addDelegate(ITEM_TYPE_ERROR_FOOTER, errorFooterAD(onRetryClick))
|
.addDelegate(ITEM_TYPE_ERROR_FOOTER, errorFooterAD(listener))
|
||||||
.addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD())
|
.addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD(listener))
|
||||||
.addDelegate(ITEM_TYPE_HEADER, listHeaderAD())
|
.addDelegate(ITEM_TYPE_HEADER, listHeaderAD())
|
||||||
.addDelegate(ITEM_TYPE_FILTER, currentFilterAD(onTagRemoveClick))
|
.addDelegate(ITEM_TYPE_FILTER, currentFilterAD(listener))
|
||||||
.addDelegate(ITEM_TYPE_HEADER_FILTER, listHeaderWithFilterAD(onFilterClickListener))
|
.addDelegate(ITEM_TYPE_HEADER_FILTER, listHeaderWithFilterAD(listener))
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DiffCallback : DiffUtil.ItemCallback<ListModel>() {
|
private class DiffCallback : DiffUtil.ItemCallback<ListModel>() {
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.koitharu.kotatsu.list.ui.adapter
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
|
import org.koitharu.kotatsu.core.model.Manga
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaTag
|
||||||
|
|
||||||
|
interface MangaListListener : OnListItemClickListener<Manga> {
|
||||||
|
|
||||||
|
fun onRetryClick(error: Throwable)
|
||||||
|
fun onTagRemoveClick(tag: MangaTag)
|
||||||
|
fun onFilterClick()
|
||||||
|
fun onEmptyActionClick()
|
||||||
|
}
|
||||||
@@ -24,10 +24,15 @@ class FilterBottomSheet : BaseBottomSheet<SheetFilterBinding>() {
|
|||||||
) {
|
) {
|
||||||
parametersOf(
|
parametersOf(
|
||||||
requireArguments().getParcelable<MangaSource>(ARG_SOURCE),
|
requireArguments().getParcelable<MangaSource>(ARG_SOURCE),
|
||||||
requireArguments().getParcelable<FilterState>(ARG_STATE),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val state = requireArguments().getParcelable<FilterState>(ARG_STATE)
|
||||||
|
viewModel.updateState(state)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetFilterBinding {
|
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetFilterBinding {
|
||||||
return SheetFilterBinding.inflate(inflater, container, false)
|
return SheetFilterBinding.inflate(inflater, container, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,28 +11,24 @@ import org.koitharu.kotatsu.base.ui.BaseViewModel
|
|||||||
import org.koitharu.kotatsu.core.model.MangaTag
|
import org.koitharu.kotatsu.core.model.MangaTag
|
||||||
import org.koitharu.kotatsu.core.model.SortOrder
|
import org.koitharu.kotatsu.core.model.SortOrder
|
||||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||||
|
import org.koitharu.kotatsu.utils.ext.replaceWith
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class FilterViewModel(
|
class FilterViewModel(
|
||||||
private val repository: RemoteMangaRepository,
|
private val repository: RemoteMangaRepository,
|
||||||
dataRepository: MangaDataRepository,
|
dataRepository: MangaDataRepository,
|
||||||
state: FilterState,
|
|
||||||
) : BaseViewModel(), OnFilterChangedListener {
|
) : BaseViewModel(), OnFilterChangedListener {
|
||||||
|
|
||||||
val filter = MutableLiveData<List<FilterItem>>()
|
val filter = MutableLiveData<List<FilterItem>>()
|
||||||
val result = MutableLiveData<FilterState>()
|
val result = MutableLiveData<FilterState>()
|
||||||
private var job: Job? = null
|
private var job: Job? = null
|
||||||
private var selectedSortOrder: SortOrder? = state.sortOrder
|
private var selectedSortOrder: SortOrder? = repository.sortOrders.firstOrNull()
|
||||||
private val selectedTags = HashSet(state.tags)
|
private val selectedTags = HashSet<MangaTag>()
|
||||||
private val localTagsDeferred = viewModelScope.async(Dispatchers.Default) {
|
private val localTagsDeferred = viewModelScope.async(Dispatchers.Default) {
|
||||||
dataRepository.findTags(repository.source)
|
dataRepository.findTags(repository.source)
|
||||||
}
|
}
|
||||||
private var availableTagsDeferred = loadTagsAsync()
|
private var availableTagsDeferred = loadTagsAsync()
|
||||||
|
|
||||||
init {
|
|
||||||
showFilter()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSortItemClick(item: FilterItem.Sort) {
|
override fun onSortItemClick(item: FilterItem.Sort) {
|
||||||
selectedSortOrder = item.order
|
selectedSortOrder = item.order
|
||||||
updateFilters()
|
updateFilters()
|
||||||
@@ -49,6 +45,18 @@ class FilterViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateState(state: FilterState?) {
|
||||||
|
if (state != null) {
|
||||||
|
selectedSortOrder = state.sortOrder
|
||||||
|
selectedTags.replaceWith(state.tags)
|
||||||
|
}
|
||||||
|
if (job == null) {
|
||||||
|
showFilter()
|
||||||
|
} else {
|
||||||
|
updateFilters()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
private fun updateFilters() {
|
private fun updateFilters() {
|
||||||
val previousJob = job
|
val previousJob = job
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ import androidx.annotation.StringRes
|
|||||||
data class EmptyState(
|
data class EmptyState(
|
||||||
@DrawableRes val icon: Int,
|
@DrawableRes val icon: Int,
|
||||||
@StringRes val textPrimary: Int,
|
@StringRes val textPrimary: Int,
|
||||||
@StringRes val textSecondary: Int
|
@StringRes val textSecondary: Int,
|
||||||
|
@StringRes val actionStringRes: Int,
|
||||||
) : ListModel
|
) : ListModel
|
||||||
@@ -62,6 +62,21 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<List<@JvmS
|
|||||||
|
|
||||||
override fun onScrolledToEnd() = Unit
|
override fun onScrolledToEnd() = Unit
|
||||||
|
|
||||||
|
override fun onEmptyActionClick() {
|
||||||
|
try {
|
||||||
|
importCall.launch(arrayOf("*/*"))
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
Snackbar.make(
|
||||||
|
binding.recyclerView,
|
||||||
|
R.string.operation_not_supported,
|
||||||
|
Snackbar.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
inflater.inflate(R.menu.opt_local, menu)
|
inflater.inflate(R.menu.opt_local, menu)
|
||||||
super.onCreateOptionsMenu(menu, inflater)
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
@@ -70,18 +85,7 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<List<@JvmS
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.action_import -> {
|
R.id.action_import -> {
|
||||||
try {
|
onEmptyActionClick()
|
||||||
importCall.launch(arrayOf("*/*"))
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
Snackbar.make(
|
|
||||||
binding.recyclerView,
|
|
||||||
R.string.operation_not_supported,
|
|
||||||
Snackbar.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
|
|||||||
@@ -45,9 +45,10 @@ class LocalListViewModel(
|
|||||||
list == null -> listOf(LoadingState)
|
list == null -> listOf(LoadingState)
|
||||||
list.isEmpty() -> listOf(
|
list.isEmpty() -> listOf(
|
||||||
EmptyState(
|
EmptyState(
|
||||||
R.drawable.ic_storage,
|
icon = R.drawable.ic_storage,
|
||||||
R.string.text_local_holder_primary,
|
textPrimary = R.string.text_local_holder_primary,
|
||||||
R.string.text_local_holder_secondary
|
textSecondary = R.string.text_local_holder_secondary,
|
||||||
|
actionStringRes = R.string._import,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else -> ArrayList<ListModel>(list.size + 1).apply {
|
else -> ArrayList<ListModel>(list.size + 1).apply {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ val remoteListModule
|
|||||||
FilterViewModel(
|
FilterViewModel(
|
||||||
repository = get<MangaRepository>(named(params.get<MangaSource>())) as RemoteMangaRepository,
|
repository = get<MangaRepository>(named(params.get<MangaSource>())) as RemoteMangaRepository,
|
||||||
dataRepository = get(),
|
dataRepository = get(),
|
||||||
state = params.get(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.R
|
|||||||
import org.koitharu.kotatsu.core.model.MangaSource
|
import org.koitharu.kotatsu.core.model.MangaSource
|
||||||
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
||||||
import org.koitharu.kotatsu.list.ui.filter.FilterBottomSheet
|
import org.koitharu.kotatsu.list.ui.filter.FilterBottomSheet
|
||||||
|
import org.koitharu.kotatsu.list.ui.filter.FilterState
|
||||||
import org.koitharu.kotatsu.reader.ui.SimpleSettingsActivity
|
import org.koitharu.kotatsu.reader.ui.SimpleSettingsActivity
|
||||||
import org.koitharu.kotatsu.utils.ext.parcelableArgument
|
import org.koitharu.kotatsu.utils.ext.parcelableArgument
|
||||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||||
@@ -65,6 +66,10 @@ class RemoteListFragment : MangaListFragment(), FragmentResultListener {
|
|||||||
FilterBottomSheet.show(childFragmentManager, source, viewModel.filter)
|
FilterBottomSheet.show(childFragmentManager, source, viewModel.filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onEmptyActionClick() {
|
||||||
|
viewModel.applyFilter(FilterState(viewModel.filter.sortOrder, emptySet()))
|
||||||
|
}
|
||||||
|
|
||||||
override fun onFragmentResult(requestKey: String, result: Bundle) {
|
override fun onFragmentResult(requestKey: String, result: Bundle) {
|
||||||
when (requestKey) {
|
when (requestKey) {
|
||||||
FilterBottomSheet.REQUEST_KEY -> viewModel.applyFilter(
|
FilterBottomSheet.REQUEST_KEY -> viewModel.applyFilter(
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class RemoteListViewModel(
|
|||||||
when {
|
when {
|
||||||
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
|
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
|
||||||
list == null -> listOf(LoadingState)
|
list == null -> listOf(LoadingState)
|
||||||
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_book_cross, R.string.nothing_found, R.string.empty))
|
list.isEmpty() -> createEmptyState()
|
||||||
else -> {
|
else -> {
|
||||||
val result = ArrayList<ListModel>(list.size + 3)
|
val result = ArrayList<ListModel>(list.size + 3)
|
||||||
result += header
|
result += header
|
||||||
@@ -126,4 +126,13 @@ class RemoteListViewModel(
|
|||||||
CurrentFilterModel(tags.map { ChipsView.ChipModel(0, it.title, it) })
|
CurrentFilterModel(tags.map { ChipsView.ChipModel(0, it.title, it) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createEmptyState() = listOf(
|
||||||
|
EmptyState(
|
||||||
|
icon = R.drawable.ic_book_cross,
|
||||||
|
textPrimary = R.string.nothing_found,
|
||||||
|
textSecondary = 0,
|
||||||
|
actionStringRes = if (filter.tags.isEmpty()) 0 else R.string.reset_filter,
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,14 @@ class SearchViewModel(
|
|||||||
when {
|
when {
|
||||||
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
|
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
|
||||||
list == null -> listOf(LoadingState)
|
list == null -> listOf(LoadingState)
|
||||||
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_book_search, R.string.nothing_found, R.string.text_search_holder_secondary))
|
list.isEmpty() -> listOf(
|
||||||
|
EmptyState(
|
||||||
|
icon = R.drawable.ic_book_search,
|
||||||
|
textPrimary = R.string.nothing_found,
|
||||||
|
textSecondary = R.string.text_search_holder_secondary,
|
||||||
|
actionStringRes = 0,
|
||||||
|
)
|
||||||
|
)
|
||||||
else -> {
|
else -> {
|
||||||
val result = ArrayList<ListModel>(list.size + 1)
|
val result = ArrayList<ListModel>(list.size + 1)
|
||||||
list.toUi(result, mode)
|
list.toUi(result, mode)
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import org.koitharu.kotatsu.list.ui.model.*
|
|||||||
import org.koitharu.kotatsu.search.domain.MangaSearchRepository
|
import org.koitharu.kotatsu.search.domain.MangaSearchRepository
|
||||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||||
import org.koitharu.kotatsu.utils.ext.onFirst
|
import org.koitharu.kotatsu.utils.ext.onFirst
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class GlobalSearchViewModel(
|
class GlobalSearchViewModel(
|
||||||
private val query: String,
|
private val query: String,
|
||||||
@@ -35,7 +34,14 @@ class GlobalSearchViewModel(
|
|||||||
when {
|
when {
|
||||||
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
|
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
|
||||||
list == null -> listOf(LoadingState)
|
list == null -> listOf(LoadingState)
|
||||||
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_book_search, R.string.nothing_found, R.string.text_search_holder_secondary))
|
list.isEmpty() -> listOf(
|
||||||
|
EmptyState(
|
||||||
|
icon = R.drawable.ic_book_search,
|
||||||
|
textPrimary = R.string.nothing_found,
|
||||||
|
textSecondary = R.string.text_search_holder_secondary,
|
||||||
|
actionStringRes = 0,
|
||||||
|
)
|
||||||
|
)
|
||||||
else -> {
|
else -> {
|
||||||
val result = ArrayList<ListModel>(list.size + 1)
|
val result = ArrayList<ListModel>(list.size + 1)
|
||||||
list.toUi(result, mode)
|
list.toUi(result, mode)
|
||||||
|
|||||||
@@ -24,11 +24,14 @@ class SuggestionsViewModel(
|
|||||||
createListModeFlow()
|
createListModeFlow()
|
||||||
) { list, mode ->
|
) { list, mode ->
|
||||||
when {
|
when {
|
||||||
list.isEmpty() -> listOf(EmptyState(
|
list.isEmpty() -> listOf(
|
||||||
icon = R.drawable.ic_book_cross,
|
EmptyState(
|
||||||
textPrimary = R.string.nothing_found,
|
icon = R.drawable.ic_book_cross,
|
||||||
textSecondary = R.string.text_suggestion_holder,
|
textPrimary = R.string.nothing_found,
|
||||||
))
|
textSecondary = R.string.text_suggestion_holder,
|
||||||
|
actionStringRes = 0,
|
||||||
|
)
|
||||||
|
)
|
||||||
else -> buildList<ListModel>(list.size + 1) {
|
else -> buildList<ListModel>(list.size + 1) {
|
||||||
add(headerModel)
|
add(headerModel)
|
||||||
list.toUi(this, mode)
|
list.toUi(this, mode)
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ import org.koin.android.ext.android.get
|
|||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
|
||||||
import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener
|
import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener
|
||||||
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
|
import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
|
||||||
import org.koitharu.kotatsu.core.model.Manga
|
import org.koitharu.kotatsu.core.model.Manga
|
||||||
|
import org.koitharu.kotatsu.core.model.MangaTag
|
||||||
import org.koitharu.kotatsu.databinding.FragmentFeedBinding
|
import org.koitharu.kotatsu.databinding.FragmentFeedBinding
|
||||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||||
|
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.main.ui.AppBarOwner
|
import org.koitharu.kotatsu.main.ui.AppBarOwner
|
||||||
import org.koitharu.kotatsu.tracker.ui.adapter.FeedAdapter
|
import org.koitharu.kotatsu.tracker.ui.adapter.FeedAdapter
|
||||||
@@ -25,7 +26,7 @@ import org.koitharu.kotatsu.utils.ext.measureHeight
|
|||||||
import org.koitharu.kotatsu.utils.progress.Progress
|
import org.koitharu.kotatsu.utils.progress.Progress
|
||||||
|
|
||||||
class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListener.Callback,
|
class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListener.Callback,
|
||||||
OnListItemClickListener<Manga> {
|
MangaListListener {
|
||||||
|
|
||||||
private val viewModel by viewModel<FeedViewModel>()
|
private val viewModel by viewModel<FeedViewModel>()
|
||||||
|
|
||||||
@@ -114,6 +115,14 @@ class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListen
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRetryClick(error: Throwable) = Unit
|
||||||
|
|
||||||
|
override fun onTagRemoveClick(tag: MangaTag) = Unit
|
||||||
|
|
||||||
|
override fun onFilterClick() = Unit
|
||||||
|
|
||||||
|
override fun onEmptyActionClick() = Unit
|
||||||
|
|
||||||
private fun onListChanged(list: List<ListModel>) {
|
private fun onListChanged(list: List<ListModel>) {
|
||||||
feedAdapter?.items = list
|
feedAdapter?.items = list
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,14 @@ class FeedViewModel(
|
|||||||
hasNextPage
|
hasNextPage
|
||||||
) { list, isHasNextPage ->
|
) { list, isHasNextPage ->
|
||||||
when {
|
when {
|
||||||
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_feed, R.string.text_empty_holder_primary, R.string.text_feed_holder))
|
list.isEmpty() -> listOf(
|
||||||
|
EmptyState(
|
||||||
|
icon = R.drawable.ic_feed,
|
||||||
|
textPrimary = R.string.text_empty_holder_primary,
|
||||||
|
textSecondary = R.string.text_feed_holder,
|
||||||
|
actionStringRes = 0,
|
||||||
|
)
|
||||||
|
)
|
||||||
isHasNextPage -> list + LoadingFooter
|
isHasNextPage -> list + LoadingFooter
|
||||||
else -> list
|
else -> list
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import androidx.lifecycle.LifecycleOwner
|
|||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
|
||||||
import org.koitharu.kotatsu.core.model.Manga
|
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.*
|
import org.koitharu.kotatsu.list.ui.adapter.*
|
||||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
|
import org.koitharu.kotatsu.list.ui.model.LoadingFooter
|
||||||
@@ -15,17 +13,17 @@ import kotlin.jvm.internal.Intrinsics
|
|||||||
class FeedAdapter(
|
class FeedAdapter(
|
||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
clickListener: OnListItemClickListener<Manga>
|
listener: MangaListListener,
|
||||||
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
|
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
delegatesManager
|
delegatesManager
|
||||||
.addDelegate(ITEM_TYPE_FEED, feedItemAD(coil, lifecycleOwner, clickListener))
|
.addDelegate(ITEM_TYPE_FEED, feedItemAD(coil, lifecycleOwner, listener))
|
||||||
.addDelegate(ITEM_TYPE_LOADING_FOOTER, loadingFooterAD())
|
.addDelegate(ITEM_TYPE_LOADING_FOOTER, loadingFooterAD())
|
||||||
.addDelegate(ITEM_TYPE_LOADING_STATE, loadingStateAD())
|
.addDelegate(ITEM_TYPE_LOADING_STATE, loadingStateAD())
|
||||||
.addDelegate(ITEM_TYPE_ERROR_FOOTER, errorFooterAD {})
|
.addDelegate(ITEM_TYPE_ERROR_FOOTER, errorFooterAD(listener))
|
||||||
.addDelegate(ITEM_TYPE_ERROR_STATE, errorStateListAD {})
|
.addDelegate(ITEM_TYPE_ERROR_STATE, errorStateListAD(listener))
|
||||||
.addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD())
|
.addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD(listener))
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DiffCallback : DiffUtil.ItemCallback<ListModel>() {
|
private class DiffCallback : DiffUtil.ItemCallback<ListModel>() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.utils.ext
|
|||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
|
|
||||||
var TextView.textAndVisible: CharSequence?
|
var TextView.textAndVisible: CharSequence?
|
||||||
@@ -18,3 +19,13 @@ var TextView.drawableStart: Drawable?
|
|||||||
val dr = compoundDrawablesRelative
|
val dr = compoundDrawablesRelative
|
||||||
setCompoundDrawablesRelativeWithIntrinsicBounds(value, dr[1], dr[2], dr[3])
|
setCompoundDrawablesRelativeWithIntrinsicBounds(value, dr[1], dr[2], dr[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun TextView.setTextAndVisible(@StringRes textResId: Int) {
|
||||||
|
if (textResId == 0) {
|
||||||
|
text = null
|
||||||
|
isGone = true
|
||||||
|
} else {
|
||||||
|
setText(textResId)
|
||||||
|
isGone = text.isNullOrEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<HorizontalScrollView
|
<org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/chips_tags"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
app:closeIconEnabled="true" />
|
||||||
<org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
|
||||||
android:id="@+id/chips_tags"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:closeIconEnabled="true"
|
|
||||||
app:singleLine="true" />
|
|
||||||
|
|
||||||
</HorizontalScrollView>
|
|
||||||
@@ -34,4 +34,14 @@
|
|||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||||
tools:text="@tools:sample/lorem[15]" />
|
tools:text="@tools:sample/lorem[15]" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_retry"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/margin_normal"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="@string/try_again"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -264,4 +264,5 @@
|
|||||||
<string name="enabled">Enabled</string>
|
<string name="enabled">Enabled</string>
|
||||||
<string name="disabled">Disabled</string>
|
<string name="disabled">Disabled</string>
|
||||||
<string name="filter_load_error">Unable to load genres list</string>
|
<string name="filter_load_error">Unable to load genres list</string>
|
||||||
|
<string name="reset_filter">Reset filter</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user