Improve filter

This commit is contained in:
Koitharu
2024-09-21 11:58:45 +03:00
committed by Mac135135
parent 26a33e5d9d
commit ea5ce23335
10 changed files with 424 additions and 435 deletions

View File

@@ -16,8 +16,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdk = 21
targetSdk = 35
versionCode = 669
versionName = '7.6-a1'
versionCode = 670
versionName = '7.6-a2'
generatedDensities = []
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
ksp {
@@ -83,7 +83,7 @@ afterEvaluate {
}
dependencies {
//noinspection GradleDependency
implementation('com.github.KotatsuApp:kotatsu-parsers:336c4a4d49') {
implementation('com.github.KotatsuApp:kotatsu-parsers:f2354957e6') {
exclude group: 'org.json', module: 'json'
}

View File

@@ -96,7 +96,7 @@ fun RangeSlider.setValuesRounded(vararg newValues: Float) {
newValue
} else {
(newValue / step).roundToInt() * step
}
}.coerceIn(valueFrom, valueTo)
}
}

View File

@@ -36,6 +36,7 @@ import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.model.YEAR_MIN
import org.koitharu.kotatsu.parsers.util.SuspendLazy
import org.koitharu.kotatsu.parsers.util.ifZero
import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment
import org.koitharu.kotatsu.search.domain.MangaSearchRepository
import java.util.Calendar
@@ -238,7 +239,7 @@ class FilterCoordinator @Inject constructor(
}.map { selected ->
FilterProperty(
availableItems = listOf(YEAR_MIN, MAX_YEAR),
selectedItems = setOf(selected.yearFrom, selected.yearTo),
selectedItems = setOf(selected.yearFrom.ifZero { YEAR_MIN }, selected.yearTo.ifZero { MAX_YEAR }),
)
}.stateIn(coroutineScope, SharingStarted.Lazily, FilterProperty.LOADING)
} else {
@@ -258,6 +259,7 @@ class FilterCoordinator @Inject constructor(
fun setSortOrder(newSortOrder: SortOrder) {
currentSortOrder.value = newSortOrder
repository.defaultSortOrder = newSortOrder
}
fun set(value: MangaListFilter) {
@@ -276,6 +278,12 @@ class FilterCoordinator @Inject constructor(
}
}
fun setOriginalLocale(value: Locale?) {
currentListFilter.update { oldValue ->
oldValue.copy(originalLocale = value)
}
}
fun setYear(value: Int) {
currentListFilter.update { oldValue ->
oldValue.copy(year = value)

View File

@@ -0,0 +1,104 @@
package org.koitharu.kotatsu.filter.ui
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.annotation.AttrRes
import androidx.core.content.ContextCompat
import androidx.core.content.withStyledAttributes
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.setPadding
import androidx.core.widget.TextViewCompat
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.drawableStart
import org.koitharu.kotatsu.core.util.ext.getThemeColorStateList
import org.koitharu.kotatsu.core.util.ext.setThemeTextAppearance
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ViewFilterFieldBinding
import java.util.LinkedList
import com.google.android.material.R as materialR
class FilterFieldLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyleAttr: Int = 0,
) : RelativeLayout(context, attrs) {
private val contentViews = LinkedList<View>()
private val binding = ViewFilterFieldBinding.inflate(LayoutInflater.from(context), this)
private var errorView: TextView? = null
private var isInitialized = true
init {
context.withStyledAttributes(attrs, R.styleable.FilterFieldLayout, defStyleAttr) {
binding.textViewTitle.text = getString(R.styleable.FilterFieldLayout_title)
binding.buttonMore.isInvisible = !getBoolean(R.styleable.FilterFieldLayout_showMoreButton, false)
}
}
override fun onViewAdded(child: View) {
super.onViewAdded(child)
if (!isInitialized) {
return
}
assert(child.id != View.NO_ID)
val lp = (child.layoutParams as? LayoutParams) ?: (generateDefaultLayoutParams() as LayoutParams)
lp.alignWithParent = true
lp.width = 0
lp.addRule(ALIGN_PARENT_START)
lp.addRule(ALIGN_PARENT_END)
lp.addRule(BELOW, contentViews.lastOrNull()?.id ?: binding.textViewTitle.id)
child.layoutParams = lp
contentViews.add(child)
}
override fun onViewRemoved(child: View?) {
super.onViewRemoved(child)
contentViews.remove(child)
}
fun setValueText(valueText: String?) {
if (!binding.buttonMore.isVisible) {
binding.textViewValue.textAndVisible = valueText
}
}
fun setError(errorMessage: String?) {
if (errorMessage == null && errorView == null) {
return
}
getErrorLabel().textAndVisible = errorMessage
}
fun setOnMoreButtonClickListener(clickListener: OnClickListener?) {
binding.buttonMore.setOnClickListener(clickListener)
}
private fun getErrorLabel(): TextView {
errorView?.let {
return it
}
val label = TextView(context)
label.id = R.id.textView_error
label.compoundDrawablePadding = resources.getDimensionPixelOffset(R.dimen.screen_padding)
label.gravity = Gravity.CENTER_VERTICAL or Gravity.START
label.setPadding(resources.getDimensionPixelOffset(R.dimen.margin_small))
label.setThemeTextAppearance(
materialR.attr.textAppearanceBodySmall,
materialR.style.TextAppearance_Material3_BodySmall,
)
label.drawableStart = ContextCompat.getDrawable(context, R.drawable.ic_error_small)
TextViewCompat.setCompoundDrawableTintList(
label,
context.getThemeColorStateList(materialR.attr.colorControlNormal),
)
addView(errorView)
errorView = label
return label
}
}

View File

@@ -29,6 +29,8 @@ data class FilterProperty<out T>(
fun isEmpty(): Boolean = availableItems.isEmpty()
fun isEmptyAndSuccess(): Boolean = availableItems.isEmpty() && error == null
companion object {
val LOADING = FilterProperty<Nothing>(

View File

@@ -7,17 +7,13 @@ import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.annotation.IdRes
import androidx.core.view.isGone
import androidx.core.view.updatePadding
import androidx.fragment.app.FragmentManager
import com.google.android.material.button.MaterialButtonToggleGroup
import com.google.android.material.chip.Chip
import com.google.android.material.slider.BaseOnChangeListener
import com.google.android.material.slider.RangeSlider
import com.google.android.material.slider.Slider
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.SortDirection
import org.koitharu.kotatsu.core.model.titleResId
import org.koitharu.kotatsu.core.ui.model.titleRes
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
@@ -29,7 +25,6 @@ import org.koitharu.kotatsu.core.util.ext.parentView
import org.koitharu.kotatsu.core.util.ext.setValueRounded
import org.koitharu.kotatsu.core.util.ext.setValuesRounded
import org.koitharu.kotatsu.core.util.ext.showDistinct
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.SheetFilterBinding
import org.koitharu.kotatsu.filter.ui.FilterCoordinator
import org.koitharu.kotatsu.filter.ui.model.FilterProperty
@@ -43,11 +38,10 @@ import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.model.YEAR_UNKNOWN
import org.koitharu.kotatsu.parsers.util.toIntUp
import java.util.Locale
import com.google.android.material.R as materialR
class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
AdapterView.OnItemSelectedListener,
ChipsView.OnChipClickListener, MaterialButtonToggleGroup.OnButtonCheckedListener {
ChipsView.OnChipClickListener {
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetFilterBinding {
return SheetFilterBinding.inflate(inflater, container, false)
@@ -63,8 +57,8 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
}
val filter = requireFilter()
filter.sortOrder.observe(viewLifecycleOwner, this::onSortOrderChanged)
// filter.filterSortDirection.observe(viewLifecycleOwner, this::onSortDirectionChanged)
filter.locale.observe(viewLifecycleOwner, this::onLocaleChanged)
filter.originalLocale.observe(viewLifecycleOwner, this::onOriginalLocaleChanged)
filter.tags.observe(viewLifecycleOwner, this::onTagsChanged)
filter.tagsExcluded.observe(viewLifecycleOwner, this::onTagsExcludedChanged)
filter.states.observe(viewLifecycleOwner, this::onStateChanged)
@@ -75,6 +69,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
filter.yearRange.observe(viewLifecycleOwner, this::onYearRangeChanged)
binding.spinnerLocale.onItemSelectedListener = this
binding.spinnerOriginalLocale.onItemSelectedListener = this
binding.spinnerOrder.onItemSelectedListener = this
binding.chipsState.onChipClickListener = this
binding.chipsTypes.onChipClickListener = this
@@ -83,24 +78,20 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
binding.chipsGenresExclude.onChipClickListener = this
binding.sliderYear.addOnChangeListener(this::onSliderValueChange)
binding.sliderYearsRange.addOnChangeListener(this::onRangeSliderValueChange)
binding.layoutSortDirection.addOnButtonCheckedListener(this)
}
override fun onButtonChecked(group: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean) {
if (isChecked) {
// setSortDirection(getSortDirection(checkedId) ?: return)
binding.layoutGenres.setOnMoreButtonClickListener {
TagsCatalogSheet.show(getChildFragmentManager(), isExcludeTag = false)
}
binding.layoutGenresExclude.setOnMoreButtonClickListener {
TagsCatalogSheet.show(getChildFragmentManager(), isExcludeTag = true)
}
}
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
val filter = requireFilter()
when (parent.id) {
R.id.spinner_order -> {
val value = filter.sortOrder.value.availableItems[position]
filter.setSortOrder(value)
}
R.id.spinner_order -> filter.setSortOrder(filter.sortOrder.value.availableItems[position])
R.id.spinner_locale -> filter.setLocale(filter.locale.value.availableItems[position])
R.id.spinner_original_locale -> filter.setOriginalLocale(filter.originalLocale.value.availableItems[position])
}
}
@@ -130,8 +121,12 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
val filter = requireFilter()
when (slider.id) {
R.id.slider_yearsRange -> filter.setYearRange(
valueFrom = slider.valueFrom.toInt(),
valueTo = slider.valueTo.toInt(),
valueFrom = slider.values.firstOrNull()?.let {
if (it <= slider.valueFrom) YEAR_UNKNOWN else it.toInt()
} ?: YEAR_UNKNOWN,
valueTo = slider.values.lastOrNull()?.let {
if (it >= slider.valueTo) YEAR_UNKNOWN else it.toInt()
} ?: YEAR_UNKNOWN,
)
}
}
@@ -154,8 +149,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
private fun onSortOrderChanged(value: FilterProperty<SortOrder>) {
val b = viewBinding ?: return
b.textViewOrderTitle.isGone = value.isEmpty()
b.cardOrder.isGone = value.isEmpty()
b.layoutOrder.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
@@ -172,29 +166,9 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
}
}
private fun onSortDirectionChanged(value: FilterProperty<SortDirection>) {
val b = viewBinding ?: return
b.layoutSortDirection.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
val selected = value.selectedItems.single()
b.buttonOrderAsc.isEnabled = SortDirection.ASC in value.availableItems
b.buttonOrderDesc.isEnabled = SortDirection.DESC in value.availableItems
b.layoutSortDirection.removeOnButtonCheckedListener(this)
b.layoutSortDirection.check(
when (selected) {
SortDirection.ASC -> R.id.button_order_asc
SortDirection.DESC -> R.id.button_order_desc
},
)
b.layoutSortDirection.addOnButtonCheckedListener(this)
}
private fun onLocaleChanged(value: FilterProperty<Locale?>) {
val b = viewBinding ?: return
b.textViewLocaleTitle.isGone = value.isEmpty()
b.cardLocale.isGone = value.isEmpty()
b.layoutLocale.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
@@ -211,83 +185,61 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
}
}
private fun onTagsChanged(value: FilterProperty<MangaTag>) {
private fun onOriginalLocaleChanged(value: FilterProperty<Locale?>) {
val b = viewBinding ?: return
b.textViewGenresTitle.isGone = value.isEmpty()
b.chipsGenres.isGone = value.isEmpty()
b.textViewGenresHint.textAndVisible = value.error?.getDisplayMessage(resources)
b.layoutOriginalLocale.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
val chips = ArrayList<ChipsView.ChipModel>(value.selectedItems.size + value.availableItems.size + 1)
value.selectedItems.mapTo(chips) { tag ->
val selected = value.selectedItems.singleOrNull()
b.spinnerOriginalLocale.adapter = ArrayAdapter(
b.spinnerOriginalLocale.context,
android.R.layout.simple_spinner_dropdown_item,
android.R.id.text1,
value.availableItems.map { it.getDisplayName(b.spinnerOriginalLocale.context) },
)
val selectedIndex = value.availableItems.indexOf(selected)
if (selectedIndex >= 0) {
b.spinnerOriginalLocale.setSelection(selectedIndex, false)
}
}
private fun onTagsChanged(value: FilterProperty<MangaTag>) {
val b = viewBinding ?: return
b.layoutGenres.isGone = value.isEmptyAndSuccess()
b.layoutGenres.setError(value.error?.getDisplayMessage(resources))
if (value.isEmpty()) {
return
}
val chips = value.availableItems.map { tag ->
ChipsView.ChipModel(
title = tag.title,
isChecked = true,
isChecked = tag in value.selectedItems,
data = tag,
)
}
value.availableItems.mapNotNullTo(chips) { tag ->
if (tag !in value.selectedItems) {
ChipsView.ChipModel(
title = tag.title,
isChecked = false,
data = tag,
)
} else {
null
}
}
chips.add(
ChipsView.ChipModel(
title = getString(R.string.more),
icon = materialR.drawable.abc_ic_menu_overflow_material,
),
)
b.chipsGenres.setChips(chips)
}
private fun onTagsExcludedChanged(value: FilterProperty<MangaTag>) {
val b = viewBinding ?: return
b.textViewGenresExcludeTitle.isGone = value.isEmpty()
b.chipsGenresExclude.isGone = value.isEmpty()
b.layoutGenresExclude.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
val chips = ArrayList<ChipsView.ChipModel>(value.selectedItems.size + value.availableItems.size + 1)
value.selectedItems.mapTo(chips) { tag ->
val chips = value.availableItems.map { tag ->
ChipsView.ChipModel(
tint = 0,
title = tag.title,
icon = 0,
isChecked = true,
isChecked = tag in value.selectedItems,
data = tag,
)
}
value.availableItems.mapNotNullTo(chips) { tag ->
if (tag !in value.selectedItems) {
ChipsView.ChipModel(
title = tag.title,
isChecked = false,
data = tag,
)
} else {
null
}
}
chips.add(
ChipsView.ChipModel(
title = getString(R.string.more),
icon = materialR.drawable.abc_ic_menu_overflow_material,
),
)
b.chipsGenresExclude.setChips(chips)
}
private fun onStateChanged(value: FilterProperty<MangaState>) {
val b = viewBinding ?: return
b.textViewStateTitle.isGone = value.isEmpty()
b.chipsState.isGone = value.isEmpty()
b.layoutState.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
@@ -303,8 +255,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
private fun onContentTypesChanged(value: FilterProperty<ContentType>) {
val b = viewBinding ?: return
b.textViewTypesTitle.isGone = value.isEmpty()
b.chipsTypes.isGone = value.isEmpty()
b.layoutTypes.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
@@ -320,8 +271,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
private fun onContentRatingChanged(value: FilterProperty<ContentRating>) {
val b = viewBinding ?: return
b.textViewContentRatingTitle.isGone = value.isEmpty()
b.chipsContentRating.isGone = value.isEmpty()
b.layoutContentRating.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
@@ -337,8 +287,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
private fun onDemographicsChanged(value: FilterProperty<Demographic>) {
val b = viewBinding ?: return
b.textViewDemographicsTitle.isGone = value.isEmpty()
b.chipsDemographics.isGone = value.isEmpty()
b.layoutDemographics.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
@@ -354,17 +303,18 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
private fun onYearChanged(value: FilterProperty<Int>) {
val b = viewBinding ?: return
b.headerYear.isGone = value.isEmpty()
b.sliderYear.isGone = value.isEmpty()
b.layoutYear.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
val currentValue = value.selectedItems.singleOrNull() ?: YEAR_UNKNOWN
b.textViewYearValue.text = if (currentValue == YEAR_UNKNOWN) {
getString(R.string.none)
} else {
currentValue.toString()
}
b.layoutYear.setValueText(
if (currentValue == YEAR_UNKNOWN) {
getString(R.string.any)
} else {
currentValue.toString()
},
)
b.sliderYear.valueFrom = value.availableItems.first().toFloat()
b.sliderYear.valueTo = value.availableItems.last().toFloat()
b.sliderYear.setValueRounded(currentValue.toFloat())
@@ -372,8 +322,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
private fun onYearRangeChanged(value: FilterProperty<Int>) {
val b = viewBinding ?: return
b.headerYearsRange.isGone = value.isEmpty()
b.sliderYearsRange.isGone = value.isEmpty()
b.layoutYearsRange.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
@@ -381,22 +330,18 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
b.sliderYearsRange.valueTo = value.availableItems.last().toFloat()
val currentValueFrom = value.selectedItems.firstOrNull()?.toFloat() ?: b.sliderYearsRange.valueFrom
val currentValueTo = value.selectedItems.lastOrNull()?.toFloat() ?: b.sliderYearsRange.valueTo
b.textViewYearsRangeValue.text = getString(
R.string.memory_usage_pattern,
currentValueFrom.toString(),
currentValueTo.toString(),
b.layoutYearsRange.setValueText(
getString(
R.string.memory_usage_pattern,
currentValueFrom.toInt().toString(),
currentValueTo.toInt().toString(),
),
)
b.sliderYearsRange.setValuesRounded(currentValueFrom, currentValueTo)
}
private fun requireFilter() = (requireActivity() as FilterCoordinator.Owner).filterCoordinator
private fun getSortDirection(@IdRes buttonId: Int): SortDirection? = when (buttonId) {
R.id.button_order_asc -> SortDirection.ASC
R.id.button_order_desc -> SortDirection.DESC
else -> null
}
companion object {
private const val TAG = "FilterSheet"

View File

@@ -17,360 +17,239 @@
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollIndicators="top">
android:scrollIndicators="top"
tools:ignore="UnusedAttribute">
<LinearLayout
android:id="@+id/layout_body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="@dimen/screen_padding"
android:paddingHorizontal="@dimen/margin_small"
android:paddingBottom="@dimen/margin_normal">
<TextView
android:id="@+id/textView_order_title"
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_order"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/sort_order"
android:textAppearance="?textAppearanceTitleSmall"
android:visibility="gone"
tools:visibility="visible" />
app:title="@string/sort_order">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_order"
style="?materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
tools:visibility="visible">
<Spinner
android:id="@+id/spinner_order"
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_order"
style="?materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="@dimen/spinner_height"
android:minHeight="?listPreferredItemHeightSmall"
android:paddingHorizontal="8dp" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/layout_sort_direction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:baselineAligned="false"
android:orientation="horizontal"
android:visibility="gone"
android:weightSum="2"
app:selectionRequired="true"
app:singleSelection="true"
tools:visibility="visible">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_order_asc"
style="@style/Widget.Kotatsu.ToggleButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/sort_order_asc"
app:icon="@drawable/ic_sort_asc" />
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_order_desc"
style="@style/Widget.Kotatsu.ToggleButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/sort_order_desc"
app:icon="@drawable/ic_sort_desc" />
<Spinner
android:id="@+id/spinner_order"
android:layout_width="match_parent"
android:layout_height="@dimen/spinner_height"
android:minHeight="?listPreferredItemHeightSmall"
android:paddingHorizontal="8dp" />
</com.google.android.material.button.MaterialButtonToggleGroup>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/textView_locale_title"
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_locale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/language"
android:textAppearance="?textAppearanceTitleSmall"
android:visibility="gone"
tools:visibility="visible" />
android:layout_marginTop="@dimen/margin_small"
app:title="@string/language">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_locale"
style="?materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
tools:visibility="visible">
<Spinner
android:id="@+id/spinner_locale"
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_locale"
style="?materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="@dimen/spinner_height"
android:minHeight="?listPreferredItemHeightSmall"
android:paddingHorizontal="8dp"
android:popupBackground="@drawable/m3_spinner_popup_background" />
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small">
</com.google.android.material.card.MaterialCardView>
<Spinner
android:id="@+id/spinner_locale"
android:layout_width="match_parent"
android:layout_height="@dimen/spinner_height"
android:minHeight="?listPreferredItemHeightSmall"
android:paddingHorizontal="8dp"
android:popupBackground="@drawable/m3_spinner_popup_background" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/textView_original_locale_title"
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_original_locale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/original_language"
android:textAppearance="?textAppearanceTitleSmall"
android:visibility="gone"
tools:visibility="visible" />
android:layout_marginTop="@dimen/margin_small"
app:title="@string/original_language">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_original_locale"
style="?materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
tools:visibility="visible">
<Spinner
android:id="@+id/spinner_original_locale"
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_original_locale"
style="?materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="@dimen/spinner_height"
android:minHeight="?listPreferredItemHeightSmall"
android:paddingHorizontal="8dp"
android:popupBackground="@drawable/m3_spinner_popup_background" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/textView_genres_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/genres"
android:textAppearance="?textAppearanceTitleSmall"
android:visibility="gone"
tools:visibility="visible" />
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_genres"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_genres_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:drawablePadding="16dp"
android:gravity="center_vertical"
android:paddingVertical="8dp"
android:textAppearance="?textAppearanceBodySmall"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_error_small"
app:drawableTint="?attr/colorControlNormal"
tools:text="@string/error_multiple_genres_not_supported"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_genresExclude_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/genres_exclude"
android:textAppearance="?textAppearanceTitleSmall"
android:visibility="gone"
tools:visibility="visible" />
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_genresExclude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_types_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/type"
android:textAppearance="?textAppearanceTitleSmall"
android:visibility="gone"
tools:visibility="visible" />
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_types"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_state_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/state"
android:textAppearance="?textAppearanceTitleSmall"
android:visibility="gone"
tools:visibility="visible" />
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_contentRating_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/content_rating"
android:textAppearance="?textAppearanceTitleSmall"
android:visibility="gone"
tools:visibility="visible" />
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_contentRating"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_demographics_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/demographics"
android:textAppearance="?textAppearanceTitleSmall"
android:visibility="gone"
tools:visibility="visible" />
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_demographics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:visibility="gone"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
tools:visibility="visible" />
<LinearLayout
android:id="@+id/header_year"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:baselineAligned="true"
android:baselineAlignedChildIndex="0"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/textView_year"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:text="@string/year"
android:textAppearance="?textAppearanceTitleSmall" />
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small">
<TextView
android:id="@+id/textView_year_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
tools:text="2024" />
<Spinner
android:id="@+id/spinner_original_locale"
android:layout_width="match_parent"
android:layout_height="@dimen/spinner_height"
android:minHeight="?listPreferredItemHeightSmall"
android:paddingHorizontal="8dp"
android:popupBackground="@drawable/m3_spinner_popup_background" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.slider.Slider
android:id="@+id/slider_year"
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_genres"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:stepSize="1"
android:visibility="gone"
app:labelBehavior="gone"
app:tickVisible="true"
tools:value="2020"
tools:valueFrom="1900"
tools:valueTo="2090"
tools:visibility="visible" />
android:layout_marginTop="@dimen/margin_small"
app:showMoreButton="true"
app:title="@string/genres">
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_genres"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
<LinearLayout
android:id="@+id/header_yearsRange"
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_genresExclude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:baselineAligned="true"
android:baselineAlignedChildIndex="0"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
android:layout_marginTop="@dimen/margin_small"
app:showMoreButton="true"
app:title="@string/genres_exclude">
<TextView
android:id="@+id/textView_yearsRange"
android:layout_width="0dp"
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_genresExclude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:text="@string/year"
android:textAppearance="?textAppearanceTitleSmall" />
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
<TextView
android:id="@+id/textView_yearsRange_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
tools:text="2024" />
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
</LinearLayout>
<com.google.android.material.slider.RangeSlider
android:id="@+id/slider_yearsRange"
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_types"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:stepSize="1"
android:visibility="gone"
app:labelBehavior="gone"
app:tickVisible="true"
tools:valueFrom="1900"
tools:valueTo="2090"
tools:visibility="visible" />
android:layout_marginTop="@dimen/margin_small"
app:title="@string/type">
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_types"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
app:title="@string/state">
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_contentRating"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
app:title="@string/content_rating">
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_contentRating"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_demographics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
app:title="@string/demographics">
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_demographics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_year"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
app:title="@string/year">
<com.google.android.material.slider.Slider
android:id="@+id/slider_year"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stepSize="1"
app:labelBehavior="gone"
app:tickVisible="true"
tools:value="2020"
tools:valueFrom="1900"
tools:valueTo="2090" />
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<org.koitharu.kotatsu.filter.ui.FilterFieldLayout
android:id="@+id/layout_yearsRange"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
app:title="@string/years">
<com.google.android.material.slider.RangeSlider
android:id="@+id/slider_yearsRange"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:stepSize="1"
app:labelBehavior="gone"
app:tickVisible="true"
tools:valueFrom="1900"
tools:valueTo="2090" />
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:parentTag="android.widget.RelativeLayout">
<TextView
android:id="@+id/textView_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignWithParentIfMissing="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_toStartOf="@id/button_more"
android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
tools:text="@string/genres" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_more"
style="@style/Widget.Kotatsu.Button.More.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/textView_title"
android:layout_alignParentEnd="true"
android:text="@string/more"
android:visibility="invisible"
tools:visibility="visible" />
<TextView
android:id="@+id/textView_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/textView_title"
android:layout_alignParentEnd="true"
android:paddingEnd="@dimen/margin_small"
android:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
android:visibility="gone"
tools:visibility="visible" />
</merge>

View File

@@ -172,4 +172,9 @@
<attr name="android:progress" />
</declare-styleable>
<declare-styleable name="FilterFieldLayout">
<attr name="title" />
<attr name="showMoreButton" format="boolean" />
</declare-styleable>
</resources>

View File

@@ -724,4 +724,6 @@
<string name="demographic_shoujo">Shoujo</string>
<string name="demographic_seinen">Seinen</string>
<string name="demographic_josei">Josei</string>
<string name="years">Years</string>
<string name="any">Any</string>
</resources>