Improve filter

master
Koitharu 2 years ago
parent b73e44874d
commit 98314960cf
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -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'
}

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

@ -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)

@ -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
}
}

@ -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>(

@ -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,122 +166,80 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
}
}
private fun onSortDirectionChanged(value: FilterProperty<SortDirection>) {
private fun onLocaleChanged(value: FilterProperty<Locale?>) {
val b = viewBinding ?: return
b.layoutSortDirection.isGone = value.isEmpty()
b.layoutLocale.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
},
val selected = value.selectedItems.singleOrNull()
b.spinnerLocale.adapter = ArrayAdapter(
b.spinnerLocale.context,
android.R.layout.simple_spinner_dropdown_item,
android.R.id.text1,
value.availableItems.map { it.getDisplayName(b.spinnerLocale.context) },
)
b.layoutSortDirection.addOnButtonCheckedListener(this)
val selectedIndex = value.availableItems.indexOf(selected)
if (selectedIndex >= 0) {
b.spinnerLocale.setSelection(selectedIndex, false)
}
}
private fun onLocaleChanged(value: FilterProperty<Locale?>) {
private fun onOriginalLocaleChanged(value: FilterProperty<Locale?>) {
val b = viewBinding ?: return
b.textViewLocaleTitle.isGone = value.isEmpty()
b.cardLocale.isGone = value.isEmpty()
b.layoutOriginalLocale.isGone = value.isEmpty()
if (value.isEmpty()) {
return
}
val selected = value.selectedItems.singleOrNull()
b.spinnerLocale.adapter = ArrayAdapter(
b.spinnerLocale.context,
b.spinnerOriginalLocale.adapter = ArrayAdapter(
b.spinnerOriginalLocale.context,
android.R.layout.simple_spinner_dropdown_item,
android.R.id.text1,
value.availableItems.map { it.getDisplayName(b.spinnerLocale.context) },
value.availableItems.map { it.getDisplayName(b.spinnerOriginalLocale.context) },
)
val selectedIndex = value.availableItems.indexOf(selected)
if (selectedIndex >= 0) {
b.spinnerLocale.setSelection(selectedIndex, false)
b.spinnerOriginalLocale.setSelection(selectedIndex, false)
}
}
private fun onTagsChanged(value: FilterProperty<MangaTag>) {
val b = viewBinding ?: return
b.textViewGenresTitle.isGone = value.isEmpty()
b.chipsGenres.isGone = value.isEmpty()
b.textViewGenresHint.textAndVisible = value.error?.getDisplayMessage(resources)
b.layoutGenres.isGone = value.isEmptyAndSuccess()
b.layoutGenres.setError(value.error?.getDisplayMessage(resources))
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(
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"

@ -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" />
<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">
app:title="@string/sort_order">
<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" />
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small">
<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.card.MaterialCardView>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/layout_sort_direction"
</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="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" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_order_desc"
style="@style/Widget.Kotatsu.ToggleButton"
android:layout_width="0dp"
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_weight="1"
android:text="@string/sort_order_desc"
app:icon="@drawable/ic_sort_desc" />
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small">
</com.google.android.material.button.MaterialButtonToggleGroup>
<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" />
<TextView
android:id="@+id/textView_locale_title"
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" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_locale"
style="?materialCardViewOutlinedStyle"
</com.google.android.material.card.MaterialCardView>
</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:visibility="gone"
tools:visibility="visible">
android:layout_marginTop="@dimen/margin_small"
app:title="@string/original_language">
<Spinner
android:id="@+id/spinner_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" />
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_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" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/textView_original_locale_title"
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" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_original_locale"
style="?materialCardViewOutlinedStyle"
</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:visibility="gone"
tools:visibility="visible">
android:layout_marginTop="@dimen/margin_small"
app:showMoreButton="true"
app:title="@string/genres">
<Spinner
android:id="@+id/spinner_original_locale"
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_genres"
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"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
</com.google.android.material.card.MaterialCardView>
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<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"
<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:visibility="gone"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
tools:visibility="visible" />
android:layout_marginTop="@dimen/margin_small"
app:showMoreButton="true"
app:title="@string/genres_exclude">
<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" />
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_genresExclude"
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" />
<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" />
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<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"
<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:visibility="gone"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
tools:visibility="visible" />
android:layout_marginTop="@dimen/margin_small"
app:title="@string/type">
<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" />
<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" />
<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"
</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_normal"
android:visibility="gone"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
tools:visibility="visible" />
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" />
<LinearLayout
android:id="@+id/header_year"
</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_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" />
<TextView
android:id="@+id/textView_year_value"
android:layout_width="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:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
tools:text="2024" />
android:layout_marginHorizontal="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
</LinearLayout>
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<com.google.android.material.slider.Slider
android:id="@+id/slider_year"
<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_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" />
<LinearLayout
android:id="@+id/header_yearsRange"
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_normal"
android:baselineAligned="true"
android:baselineAlignedChildIndex="0"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/textView_yearsRange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:text="@string/year"
android:textAppearance="?textAppearanceTitleSmall" />
<TextView
android:id="@+id/textView_yearsRange_value"
android:layout_width="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:singleLine="true"
android:textAppearance="?textAppearanceBodyMedium"
tools:text="2024" />
android:stepSize="1"
app:labelBehavior="gone"
app:tickVisible="true"
tools:value="2020"
tools:valueFrom="1900"
tools:valueTo="2090" />
</LinearLayout>
</org.koitharu.kotatsu.filter.ui.FilterFieldLayout>
<com.google.android.material.slider.RangeSlider
android:id="@+id/slider_yearsRange"
<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_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/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>

@ -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>

@ -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>

@ -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>

Loading…
Cancel
Save