Quick filter in history draft implementation
This commit is contained in:
@@ -5,6 +5,7 @@ import android.util.AttributeSet
|
||||
import android.view.View.OnClickListener
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.view.children
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipDrawable
|
||||
@@ -92,7 +93,11 @@ class ChipsView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private fun bindChip(chip: Chip, model: ChipModel) {
|
||||
chip.text = model.title
|
||||
if (model.titleResId == 0) {
|
||||
chip.text = model.title
|
||||
} else {
|
||||
chip.setText(model.titleResId)
|
||||
}
|
||||
chip.isClickable = onChipClickListener != null || model.isCheckable
|
||||
chip.isCheckable = model.isCheckable
|
||||
if (model.icon == 0) {
|
||||
@@ -139,7 +144,8 @@ class ChipsView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
data class ChipModel(
|
||||
val title: CharSequence,
|
||||
val title: CharSequence? = null,
|
||||
@StringRes val titleResId: Int = 0,
|
||||
@DrawableRes val icon: Int = 0,
|
||||
val isCheckable: Boolean = false,
|
||||
@ColorRes val tint: Int = 0,
|
||||
|
||||
@@ -10,6 +10,7 @@ import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.core.db.entity.TagEntity
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
||||
|
||||
@Dao
|
||||
@@ -27,7 +28,11 @@ abstract class HistoryDao {
|
||||
@Query("SELECT * FROM history WHERE deleted_at = 0 ORDER BY updated_at DESC LIMIT :limit")
|
||||
abstract fun observeAll(limit: Int): Flow<List<HistoryWithManga>>
|
||||
|
||||
fun observeAll(order: ListSortOrder, limit: Int): Flow<List<HistoryWithManga>> {
|
||||
fun observeAll(
|
||||
order: ListSortOrder,
|
||||
filterOptions: Set<ListFilterOption>,
|
||||
limit: Int
|
||||
): Flow<List<HistoryWithManga>> {
|
||||
val orderBy = when (order) {
|
||||
ListSortOrder.LAST_READ -> "history.updated_at DESC"
|
||||
ListSortOrder.LONG_AGO_READ -> "history.updated_at ASC"
|
||||
@@ -44,8 +49,13 @@ abstract class HistoryDao {
|
||||
val query = buildString {
|
||||
append(
|
||||
"SELECT * FROM history LEFT JOIN manga ON history.manga_id = manga.manga_id " +
|
||||
"WHERE history.deleted_at = 0 GROUP BY history.manga_id ORDER BY ",
|
||||
"WHERE history.deleted_at = 0",
|
||||
)
|
||||
for (option in filterOptions) {
|
||||
append(" AND ")
|
||||
append(option.getCondition())
|
||||
}
|
||||
append(" GROUP BY history.manga_id ORDER BY ")
|
||||
append(orderBy)
|
||||
if (limit > 0) {
|
||||
append(" LIMIT ")
|
||||
@@ -147,4 +157,11 @@ abstract class HistoryDao {
|
||||
@Transaction
|
||||
@RawQuery(observedEntities = [HistoryEntity::class])
|
||||
protected abstract fun observeAllImpl(query: SupportSQLiteQuery): Flow<List<HistoryWithManga>>
|
||||
|
||||
private fun ListFilterOption.getCondition(): String = when (this) {
|
||||
ListFilterOption.NEW_CHAPTERS -> "(SELECT chapters_new FROM tracks WHERE tracks.manga_id = history.manga_id) > 0"
|
||||
ListFilterOption.FAVORITE -> "EXISTS(SELECT * FROM favourites WHERE history.manga_id = favourites.manga_id)"
|
||||
ListFilterOption.COMPLETED -> "percent >= 0.9999"
|
||||
ListFilterOption.DOWNLOADED -> throw IllegalArgumentException("Unsupported option $this")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.koitharu.kotatsu.core.prefs.ProgressIndicatorMode
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleHandle
|
||||
import org.koitharu.kotatsu.core.util.ext.mapItems
|
||||
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
@@ -76,8 +77,12 @@ class HistoryRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun observeAllWithHistory(order: ListSortOrder, limit: Int): Flow<List<MangaWithHistory>> {
|
||||
return db.getHistoryDao().observeAll(order, limit).mapItems {
|
||||
fun observeAllWithHistory(
|
||||
order: ListSortOrder,
|
||||
filterOptions: Set<ListFilterOption>,
|
||||
limit: Int
|
||||
): Flow<List<MangaWithHistory>> {
|
||||
return db.getHistoryDao().observeAll(order, filterOptions, limit).mapItems {
|
||||
MangaWithHistory(
|
||||
it.manga.toManga(it.tags.toMangaTags()),
|
||||
it.history.toMangaHistory(),
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
||||
import org.koitharu.kotatsu.list.ui.size.DynamicItemSizeResolver
|
||||
|
||||
@@ -34,6 +35,10 @@ class HistoryListFragment : MangaListFragment() {
|
||||
|
||||
override fun onScrolledToEnd() = viewModel.requestMoreItems()
|
||||
|
||||
override fun onFilterOptionClick(option: ListFilterOption) {
|
||||
viewModel.onFilterOptionClick(option)
|
||||
}
|
||||
|
||||
override fun onEmptyActionClick() {
|
||||
startActivity(NetworkManageIntent())
|
||||
}
|
||||
|
||||
@@ -21,13 +21,16 @@ import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
|
||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.core.util.ext.calculateTimeAgo
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.core.util.ext.combine
|
||||
import org.koitharu.kotatsu.core.util.ext.onFirst
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
|
||||
import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.domain.MarkAsReadUseCase
|
||||
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
||||
import org.koitharu.kotatsu.list.domain.MangaListMapper
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
@@ -36,11 +39,13 @@ import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.QuickFilter
|
||||
import org.koitharu.kotatsu.list.ui.model.TipModel
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import java.time.Instant
|
||||
import java.util.EnumSet
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -63,6 +68,8 @@ class HistoryListViewModel @Inject constructor(
|
||||
valueProducer = { historySortOrder },
|
||||
)
|
||||
|
||||
private val filterOptions = MutableStateFlow<Set<ListFilterOption>>(EnumSet.noneOf(ListFilterOption::class.java))
|
||||
|
||||
override val listMode = settings.observeAsStateFlow(
|
||||
scope = viewModelScope + Dispatchers.Default,
|
||||
key = AppSettings.KEY_LIST_MODE_HISTORY,
|
||||
@@ -86,25 +93,25 @@ class HistoryListViewModel @Inject constructor(
|
||||
)
|
||||
|
||||
override val content = combine(
|
||||
filterOptions,
|
||||
observeHistory(),
|
||||
isGroupingEnabled,
|
||||
observeListModeWithTriggers(),
|
||||
networkState,
|
||||
settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled },
|
||||
) { list, grouped, mode, online, incognito ->
|
||||
) { filters, list, grouped, mode, online, incognito ->
|
||||
when {
|
||||
list.isEmpty() -> listOf(
|
||||
EmptyState(
|
||||
icon = R.drawable.ic_empty_history,
|
||||
textPrimary = R.string.text_history_holder_primary,
|
||||
textSecondary = R.string.text_history_holder_secondary,
|
||||
actionStringRes = 0,
|
||||
),
|
||||
)
|
||||
list.isEmpty() -> {
|
||||
if (filters.isEmpty()) {
|
||||
listOf(getEmptyState(hasFilters = false))
|
||||
} else {
|
||||
listOf(filterItem(filters), getEmptyState(hasFilters = true))
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
isReady.set(true)
|
||||
mapList(list, grouped, mode, online, incognito)
|
||||
mapList(filters, list, grouped, mode, online, incognito)
|
||||
}
|
||||
}
|
||||
}.onStart {
|
||||
@@ -154,17 +161,29 @@ class HistoryListViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeHistory() = combine(sortOrder, limit, ::Pair)
|
||||
.flatMapLatest { repository.observeAllWithHistory(it.first, it.second) }
|
||||
fun onFilterOptionClick(option: ListFilterOption) {
|
||||
filterOptions.value = EnumSet.copyOf(filterOptions.value).also {
|
||||
if (option in it) {
|
||||
it.remove(option)
|
||||
} else {
|
||||
it.add(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeHistory() = combine(sortOrder, filterOptions, limit, ::Triple)
|
||||
.flatMapLatest { repository.observeAllWithHistory(it.first, it.second - ListFilterOption.DOWNLOADED, it.third) }
|
||||
|
||||
private suspend fun mapList(
|
||||
filters: Set<ListFilterOption>,
|
||||
list: List<MangaWithHistory>,
|
||||
grouped: Boolean,
|
||||
mode: ListMode,
|
||||
isOnline: Boolean,
|
||||
isIncognito: Boolean,
|
||||
): List<ListModel> {
|
||||
val result = ArrayList<ListModel>(if (grouped) (list.size * 1.4).toInt() else list.size + 2)
|
||||
val result = ArrayList<ListModel>((if (grouped) (list.size * 1.4).toInt() else list.size) + 3)
|
||||
result += filterItem(filters)
|
||||
if (isIncognito) {
|
||||
result += TipModel(
|
||||
key = AppSettings.KEY_INCOGNITO_MODE,
|
||||
@@ -185,12 +204,14 @@ class HistoryListViewModel @Inject constructor(
|
||||
actionStringRes = R.string.manage,
|
||||
)
|
||||
}
|
||||
var isEmpty = true
|
||||
for ((m, history) in list) {
|
||||
val manga = if (!isOnline && !m.isLocal) {
|
||||
val manga = if ((!isOnline && !m.isLocal) || ListFilterOption.DOWNLOADED in filters) {
|
||||
localMangaRepository.findSavedManga(m)?.manga ?: continue
|
||||
} else {
|
||||
m
|
||||
}
|
||||
isEmpty = false
|
||||
if (grouped) {
|
||||
val header = history.header(order)
|
||||
if (header != prevHeader) {
|
||||
@@ -202,6 +223,9 @@ class HistoryListViewModel @Inject constructor(
|
||||
}
|
||||
result += mangaListMapper.toListModel(manga, mode)
|
||||
}
|
||||
if (filters.isNotEmpty() && isEmpty) {
|
||||
result += getEmptyState(hasFilters = true)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -229,4 +253,32 @@ class HistoryListViewModel @Inject constructor(
|
||||
ListSortOrder.UPDATED,
|
||||
ListSortOrder.RATING -> null
|
||||
}
|
||||
|
||||
private fun filterItem(selected: Set<ListFilterOption>) = QuickFilter(
|
||||
items = ListFilterOption.HISTORY.map { option ->
|
||||
ChipsView.ChipModel(
|
||||
titleResId = option.titleResId,
|
||||
icon = option.iconResId,
|
||||
isCheckable = true,
|
||||
isChecked = option in selected,
|
||||
data = option,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
private fun getEmptyState(hasFilters: Boolean) = if (hasFilters) {
|
||||
EmptyState(
|
||||
icon = R.drawable.ic_empty_history,
|
||||
textPrimary = R.string.nothing_found,
|
||||
textSecondary = R.string.text_history_holder_secondary_filtered,
|
||||
actionStringRes = 0,
|
||||
)
|
||||
} else {
|
||||
EmptyState(
|
||||
icon = R.drawable.ic_empty_history,
|
||||
textPrimary = R.string.text_history_holder_primary,
|
||||
textSecondary = R.string.text_history_holder_secondary,
|
||||
actionStringRes = 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.koitharu.kotatsu.list.domain
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
import java.util.EnumSet
|
||||
|
||||
enum class ListFilterOption(
|
||||
@StringRes val titleResId: Int,
|
||||
@DrawableRes val iconResId: Int,
|
||||
) {
|
||||
|
||||
DOWNLOADED(R.string.on_device, R.drawable.ic_storage),
|
||||
COMPLETED(R.string.status_completed, R.drawable.ic_state_finished),
|
||||
NEW_CHAPTERS(R.string.new_chapters, R.drawable.ic_updated),
|
||||
FAVORITE(R.string.favourites, R.drawable.ic_heart_outline),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
val HISTORY: Set<ListFilterOption> = EnumSet.of(
|
||||
DOWNLOADED,
|
||||
NEW_CHAPTERS,
|
||||
FAVORITE,
|
||||
COMPLETED,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ import org.koitharu.kotatsu.databinding.FragmentListBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteSheet
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter
|
||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
||||
@@ -226,6 +227,8 @@ abstract class MangaListFragment :
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFilterOptionClick(option: ListFilterOption) = Unit
|
||||
|
||||
override fun onFilterClick(view: View?) = Unit
|
||||
|
||||
override fun onEmptyActionClick() = Unit
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
enum class ListItemType {
|
||||
|
||||
FILTER_HEADER,
|
||||
FILTER_SORT,
|
||||
FILTER_TAG,
|
||||
FILTER_TAG_MULTI,
|
||||
|
||||
@@ -24,6 +24,7 @@ open class MangaListAdapter(
|
||||
addDelegate(ListItemType.STATE_EMPTY, emptyStateListAD(coil, lifecycleOwner, listener))
|
||||
addDelegate(ListItemType.HINT_EMPTY, emptyHintAD(coil, lifecycleOwner, listener))
|
||||
addDelegate(ListItemType.HEADER, listHeaderAD(listener))
|
||||
addDelegate(ListItemType.FILTER_HEADER, quickFilterAD(listener))
|
||||
addDelegate(ListItemType.TIP, tipAD(listener))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ fun mangaListDetailedItemAD(
|
||||
source(item.source)
|
||||
enqueueWith(coil)
|
||||
}
|
||||
binding.textViewTags.text = item.tags.joinToString(separator = ", ") { it.title }
|
||||
binding.textViewTags.text = item.tags.joinToString(separator = ", ") { it.title ?: "" }
|
||||
badge = itemView.bindBadge(badge, item.counter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.koitharu.kotatsu.core.ui.widgets.TipView
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
|
||||
interface MangaListListener : MangaDetailsClickListener, ListStateHolderListener, ListHeaderClickListener,
|
||||
TipView.OnButtonClickListener {
|
||||
TipView.OnButtonClickListener, QuickFilterClickListener {
|
||||
|
||||
fun onUpdateFilter(tags: Set<MangaTag>)
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.databinding.ItemQuickFilterBinding
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.QuickFilter
|
||||
|
||||
fun quickFilterAD(
|
||||
listener: QuickFilterClickListener,
|
||||
) = adapterDelegateViewBinding<QuickFilter, ListModel, ItemQuickFilterBinding>(
|
||||
{ layoutInflater, parent -> ItemQuickFilterBinding.inflate(layoutInflater, parent, false) }
|
||||
) {
|
||||
|
||||
binding.chipsTags.onChipClickListener = ChipsView.OnChipClickListener { chip, data ->
|
||||
if (data is ListFilterOption) {
|
||||
listener.onFilterOptionClick(data)
|
||||
}
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.chipsTags.setChips(item.items)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
|
||||
interface QuickFilterClickListener {
|
||||
|
||||
fun onFilterOptionClick(option: ListFilterOption)
|
||||
}
|
||||
@@ -32,6 +32,7 @@ class TypedListSpacingDecoration(
|
||||
ListItemType.FILTER_TAG_MULTI,
|
||||
ListItemType.FILTER_STATE,
|
||||
ListItemType.FILTER_LANGUAGE,
|
||||
ListItemType.FILTER_HEADER,
|
||||
-> outRect.set(0)
|
||||
|
||||
ListItemType.HEADER,
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.koitharu.kotatsu.list.ui.model
|
||||
|
||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
|
||||
|
||||
data class QuickFilter(
|
||||
val items: List<ChipsView.ChipModel>,
|
||||
) : ListModel {
|
||||
|
||||
override fun areItemsTheSame(other: ListModel): Boolean = other is QuickFilter
|
||||
|
||||
override fun getChangePayload(previousState: ListModel) = ListModelDiffCallback.PAYLOAD_NESTED_LIST_CHANGED
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import org.koitharu.kotatsu.databinding.ActivitySearchMultiBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
|
||||
import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteSheet
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration
|
||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
||||
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
|
||||
@@ -133,6 +134,8 @@ class MultiSearchActivity :
|
||||
viewModel.retry()
|
||||
}
|
||||
|
||||
override fun onFilterOptionClick(option: ListFilterOption) = Unit
|
||||
|
||||
override fun onUpdateFilter(tags: Set<MangaTag>) = Unit
|
||||
|
||||
override fun onFilterClick(view: View?) = Unit
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.databinding.FragmentFeedBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.ui.adapter.MangaListListener
|
||||
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
@@ -94,6 +95,8 @@ class FeedFragment :
|
||||
viewModel.update()
|
||||
}
|
||||
|
||||
override fun onFilterOptionClick(option: ListFilterOption) = Unit
|
||||
|
||||
override fun onRetryClick(error: Throwable) = Unit
|
||||
|
||||
override fun onUpdateFilter(tags: Set<MangaTag>) = Unit
|
||||
|
||||
24
app/src/main/res/layout/item_quick_filter.xml
Normal file
24
app/src/main/res/layout/item_quick_filter.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<HorizontalScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:paddingHorizontal="@dimen/list_spacing"
|
||||
android:scrollbars="none">
|
||||
|
||||
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||
android:id="@+id/chips_tags"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingVertical="2dp"
|
||||
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
|
||||
app:selectionRequired="false"
|
||||
app:singleLine="true"
|
||||
app:singleSelection="false" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
@@ -96,6 +96,7 @@
|
||||
<string name="text_search_holder_secondary">Try to reformulate the query.</string>
|
||||
<string name="text_history_holder_primary">What you read will be displayed here</string>
|
||||
<string name="text_history_holder_secondary">Find what to read in the «Explore» section</string>
|
||||
<string name="text_history_holder_secondary_filtered">There are no manga matching the filters you selected</string>
|
||||
<string name="text_local_holder_primary">Save something first</string>
|
||||
<string name="text_local_holder_secondary">Save something from an online catalog or import it from a file.</string>
|
||||
<string name="manga_shelf">Shelf</string>
|
||||
|
||||
Reference in New Issue
Block a user