@ -1,39 +1,24 @@
package org.koitharu.kotatsu.filter.ui.sheet
package org.koitharu.kotatsu.filter.ui.sheet
import android.os.Build
import android.os.Bundle
import android.os.Bundle
import android.text.InputFilter
import android.view.Gravity
import android.view.LayoutInflater
import android.view.LayoutInflater
import android.view.View
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.AdapterView
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.ArrayAdapter
import android.widget.LinearLayout
import android.widget.Toast
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isGone
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.core.view.updatePadding
import com.google.android.material.chip.Chip
import com.google.android.material.chip.Chip
import com.google.android.material.slider.RangeSlider
import com.google.android.material.slider.RangeSlider
import com.google.android.material.slider.Slider
import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.titleResId
import org.koitharu.kotatsu.core.model.titleResId
import org.koitharu.kotatsu.core.nav.router
import org.koitharu.kotatsu.core.nav.router
import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog
import org.koitharu.kotatsu.core.ui.dialog.setEditText
import org.koitharu.kotatsu.core.ui.model.titleRes
import org.koitharu.kotatsu.core.ui.model.titleRes
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.util.AlphanumComparator
import org.koitharu.kotatsu.core.util.ext.consume
import org.koitharu.kotatsu.core.util.ext.consume
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.getDisplayName
import org.koitharu.kotatsu.core.util.ext.getDisplayName
@ -42,8 +27,6 @@ import org.koitharu.kotatsu.core.util.ext.parentView
import org.koitharu.kotatsu.core.util.ext.setValueRounded
import org.koitharu.kotatsu.core.util.ext.setValueRounded
import org.koitharu.kotatsu.core.util.ext.setValuesRounded
import org.koitharu.kotatsu.core.util.ext.setValuesRounded
import org.koitharu.kotatsu.databinding.SheetFilterBinding
import org.koitharu.kotatsu.databinding.SheetFilterBinding
import org.koitharu.kotatsu.filter.data.PersistableFilter
import org.koitharu.kotatsu.filter.data.PersistableFilter.Companion.MAX_TITLE_LENGTH
import org.koitharu.kotatsu.filter.ui.FilterCoordinator
import org.koitharu.kotatsu.filter.ui.FilterCoordinator
import org.koitharu.kotatsu.filter.ui.model.FilterProperty
import org.koitharu.kotatsu.filter.ui.model.FilterProperty
import org.koitharu.kotatsu.parsers.model.ContentRating
import org.koitharu.kotatsu.parsers.model.ContentRating
@ -53,499 +36,324 @@ import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.model.YEAR_UNKNOWN
import org.koitharu.kotatsu.parsers.model.YEAR_UNKNOWN
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.toIntUp
import org.koitharu.kotatsu.parsers.util.toIntUp
import java.util.Locale
import java.util.Locale
import java.util.TreeSet
class FilterSheetFragment : BaseAdaptiveSheet < SheetFilterBinding > ( ) ,
class FilterSheetFragment : BaseAdaptiveSheet < SheetFilterBinding > ( ) ,
AdapterView . OnItemSelectedListener ,
AdapterView . OnItemSelectedListener ,
View . OnClickListener ,
ChipsView . OnChipClickListener {
ChipsView . OnChipClickListener ,
ChipsView . OnChipLongClickListener ,
override fun onCreateViewBinding ( inflater : LayoutInflater , container : ViewGroup ? ) : SheetFilterBinding {
ChipsView . OnChipCloseClickListener {
return SheetFilterBinding . inflate ( inflater , container , false )
}
override fun onCreateViewBinding ( inflater : LayoutInflater , container : ViewGroup ? ) : SheetFilterBinding {
return SheetFilterBinding . inflate ( inflater , container , false )
override fun onViewBindingCreated ( binding : SheetFilterBinding , savedInstanceState : Bundle ? ) {
}
super . onViewBindingCreated ( binding , savedInstanceState )
if ( dialog == null ) {
override fun onViewBindingCreated ( binding : SheetFilterBinding , savedInstanceState : Bundle ? ) {
binding . layoutBody . updatePadding ( top = binding . layoutBody . paddingBottom )
super . onViewBindingCreated ( binding , savedInstanceState )
if ( Build . VERSION . SDK _INT >= Build . VERSION_CODES . M ) {
if ( dialog == null ) {
binding . scrollView . scrollIndicators = 0
binding . adjustForEmbeddedLayout ( )
}
}
}
val filter = FilterCoordinator . require ( this )
val filter = FilterCoordinator . require ( this )
filter . sortOrder . observe ( viewLifecycleOwner , this :: onSortOrderChanged )
filter . sortOrder . observe ( viewLifecycleOwner , this :: onSortOrderChanged )
filter . locale . observe ( viewLifecycleOwner , this :: onLocaleChanged )
filter . locale . observe ( viewLifecycleOwner , this :: onLocaleChanged )
filter . originalLocale . observe ( viewLifecycleOwner , this :: onOriginalLocaleChanged )
filter . originalLocale . observe ( viewLifecycleOwner , this :: onOriginalLocaleChanged )
filter . tags . observe ( viewLifecycleOwner , this :: onTagsChanged )
filter . tags . observe ( viewLifecycleOwner , this :: onTagsChanged )
filter . tagsExcluded . observe ( viewLifecycleOwner , this :: onTagsExcludedChanged )
filter . tagsExcluded . observe ( viewLifecycleOwner , this :: onTagsExcludedChanged )
filter . authors . observe ( viewLifecycleOwner , this :: onAuthorsChanged )
filter . states . observe ( viewLifecycleOwner , this :: onStateChanged )
filter . states . observe ( viewLifecycleOwner , this :: onStateChanged )
filter . contentTypes . observe ( viewLifecycleOwner , this :: onContentTypesChanged )
filter . contentTypes . observe ( viewLifecycleOwner , this :: onContentTypesChanged )
filter . contentRating . observe ( viewLifecycleOwner , this :: onContentRatingChanged )
filter . contentRating . observe ( viewLifecycleOwner , this :: onContentRatingChanged )
filter . demographics . observe ( viewLifecycleOwner , this :: onDemographicsChanged )
filter . demographics . observe ( viewLifecycleOwner , this :: onDemographicsChanged )
filter . year . observe ( viewLifecycleOwner , this :: onYearChanged )
filter . year . observe ( viewLifecycleOwner , this :: onYearChanged )
filter . yearRange . observe ( viewLifecycleOwner , this :: onYearRangeChanged )
filter . yearRange . observe ( viewLifecycleOwner , this :: onYearRangeChanged )
filter . savedFilters . observe ( viewLifecycleOwner , :: onSavedPresetsChanged )
binding . layoutGenres . setTitle (
if ( filter . capabilities . isMultipleTagsSupported ) {
binding . layoutGenres . setTitle (
R . string . genres
if ( filter . capabilities . isMultipleTagsSupported ) {
} else {
R . string . genres
R . string . genre
} else {
} ,
R . string . genre
)
} ,
binding . spinnerLocale . onItemSelectedListener = this
)
binding . spinnerOriginalLocale . onItemSelectedListener = this
binding . spinnerLocale . onItemSelectedListener = this
binding . spinnerOrder . onItemSelectedListener = this
binding . spinnerOriginalLocale . onItemSelectedListener = this
binding . chipsState . onChipClickListener = this
binding . spinnerOrder . onItemSelectedListener = this
binding . chipsTypes . onChipClickListener = this
binding . chipsSavedFilters . onChipClickListener = this
binding . chipsContentRating . onChipClickListener = this
binding . chipsState . onChipClickListener = this
binding . chipsDemographics . onChipClickListener = this
binding . chipsTypes . onChipClickListener = this
binding . chipsGenres . onChipClickListener = this
binding . chipsContentRating . onChipClickListener = this
binding . chipsGenresExclude . onChipClickListener = this
binding . chipsDemographics . onChipClickListener = this
binding . sliderYear . addOnChangeListener ( this :: onSliderValueChange )
binding . chipsGenres . onChipClickListener = this
binding . sliderYearsRange . addOnChangeListener ( this :: onRangeSliderValueChange )
binding . chipsGenresExclude . onChipClickListener = this
binding . layoutGenres . setOnMoreButtonClickListener {
binding . chipsAuthor . onChipClickListener = this
router . showTagsCatalogSheet ( excludeMode = false )
binding . chipsSavedFilters . onChipLongClickListener = this
}
binding . chipsSavedFilters . onChipCloseClickListener = this
binding . layoutGenresExclude . setOnMoreButtonClickListener {
binding . sliderYear . addOnChangeListener ( this :: onSliderValueChange )
router . showTagsCatalogSheet ( excludeMode = true )
binding . sliderYearsRange . addOnChangeListener ( this :: onRangeSliderValueChange )
}
binding . layoutGenres . setOnMoreButtonClickListener {
}
router . showTagsCatalogSheet ( excludeMode = false )
}
override fun onApplyWindowInsets ( v : View , insets : WindowInsetsCompat ) : WindowInsetsCompat {
binding . layoutGenresExclude . setOnMoreButtonClickListener {
val typeMask = WindowInsetsCompat . Type . systemBars ( )
router . showTagsCatalogSheet ( excludeMode = true )
viewBinding ?. scrollView ?. updatePadding (
}
bottom = insets . getInsets ( typeMask ) . bottom ,
combine (
)
filter . observe ( ) . map { it . listFilter . isNotEmpty ( ) } . distinctUntilChanged ( ) ,
return insets . consume ( v , typeMask , bottom = true )
filter . savedFilters . map { it . selectedItems . isEmpty ( ) } . distinctUntilChanged ( ) ,
}
Boolean :: and ,
) . flowOn ( Dispatchers . Default )
override fun onItemSelected ( parent : AdapterView < * > , view : View ? , position : Int , id : Long ) {
. observe ( viewLifecycleOwner ) {
val filter = FilterCoordinator . require ( this )
binding . buttonSave . isEnabled = it
when ( parent . id ) {
}
R . id . spinner _order -> filter . setSortOrder ( filter . sortOrder . value . availableItems [ position ] )
binding . buttonSave . setOnClickListener ( this )
R . id . spinner _locale -> filter . setLocale ( filter . locale . value . availableItems [ position ] )
binding . buttonDone . setOnClickListener ( this )
R . id . spinner _original _locale -> filter . setOriginalLocale ( filter . originalLocale . value . availableItems [ position ] )
}
}
}
private fun SheetFilterBinding . adjustForEmbeddedLayout ( ) {
layoutBody . updatePadding ( top = layoutBody . paddingBottom )
override fun onNothingSelected ( parent : AdapterView < * > ? ) = Unit
scrollView . scrollIndicators = 0
buttonDone . isVisible = false
private fun onSliderValueChange ( slider : Slider , value : Float , fromUser : Boolean ) {
this . root . layoutParams ?. height = ViewGroup . LayoutParams . MATCH _PARENT
if ( ! fromUser ) {
buttonSave . updateLayoutParams < LinearLayout . LayoutParams > {
return
weight = 0f
}
width = LinearLayout . LayoutParams . WRAP _CONTENT
val intValue = value . toInt ( )
gravity = Gravity . END or Gravity . CENTER _VERTICAL
val filter = FilterCoordinator . require ( this )
}
when ( slider . id ) {
}
R . id . slider _year -> filter . setYear (
if ( intValue <= slider . valueFrom . toIntUp ( ) ) {
override fun onApplyWindowInsets ( v : View , insets : WindowInsetsCompat ) : WindowInsetsCompat {
YEAR _UNKNOWN
val typeMask = WindowInsetsCompat . Type . systemBars ( )
} else {
viewBinding ?. layoutBottom ?. updateLayoutParams < ViewGroup . MarginLayoutParams > {
intValue
bottomMargin = insets . getInsets ( typeMask ) . bottom
} ,
}
)
return insets . consume ( v , typeMask , bottom = true )
}
}
}
override fun onClick ( v : View ) {
private fun onRangeSliderValueChange ( slider : RangeSlider , value : Float , fromUser : Boolean ) {
when ( v . id ) {
if ( ! fromUser ) {
R . id . button _done -> dismiss ( )
return
R . id . button _save -> onSaveFilterClick ( " " )
}
}
val filter = FilterCoordinator . require ( this )
}
when ( slider . id ) {
R . id . slider _yearsRange -> filter . setYearRange (
override fun onItemSelected ( parent : AdapterView < * > , view : View ? , position : Int , id : Long ) {
valueFrom = slider . values . firstOrNull ( ) ?. let {
val filter = FilterCoordinator . require ( this )
if ( it <= slider . valueFrom ) YEAR _UNKNOWN else it . toInt ( )
when ( parent . id ) {
} ?: YEAR _UNKNOWN ,
R . id . spinner _order -> filter . setSortOrder ( filter . sortOrder . value . availableItems [ position ] )
valueTo = slider . values . lastOrNull ( ) ?. let {
R . id . spinner _locale -> filter . setLocale ( filter . locale . value . availableItems [ position ] )
if ( it >= slider . valueTo ) YEAR _UNKNOWN else it . toInt ( )
R . id . spinner _original _locale -> filter . setOriginalLocale ( filter . originalLocale . value . availableItems [ position ] )
} ?: YEAR _UNKNOWN ,
}
)
}
}
}
override fun onNothingSelected ( parent : AdapterView < * > ? ) = Unit
override fun onChipClick ( chip : Chip , data : Any ? ) {
private fun onSliderValueChange ( slider : Slider , value : Float , fromUser : Boolean ) {
val filter = FilterCoordinator . require ( this )
if ( ! fromUser ) {
when ( data ) {
return
is MangaState -> filter . toggleState ( data , ! chip . isChecked )
}
is MangaTag -> if ( chip . parentView ?. id == R . id . chips _genresExclude ) {
val intValue = value . toInt ( )
filter . toggleTagExclude ( data , ! chip . isChecked )
val filter = FilterCoordinator . require ( this )
} else {
when ( slider . id ) {
filter . toggleTag ( data , ! chip . isChecked )
R . id . slider _year -> filter . setYear (
}
if ( intValue <= slider . valueFrom . toIntUp ( ) ) {
YEAR _UNKNOWN
is ContentType -> filter . toggleContentType ( data , ! chip . isChecked )
} else {
is ContentRating -> filter . toggleContentRating ( data , ! chip . isChecked )
intValue
is Demographic -> filter . toggleDemographic ( data , ! chip . isChecked )
} ,
null -> router . showTagsCatalogSheet ( excludeMode = chip . parentView ?. id == R . id . chips _genresExclude )
)
}
}
}
}
private fun onSortOrderChanged ( value : FilterProperty < SortOrder > ) {
private fun onRangeSliderValueChange ( slider : RangeSlider , value : Float , fromUser : Boolean ) {
val b = viewBinding ?: return
if ( ! fromUser ) {
b . layoutOrder . isGone = value . isEmpty ( )
return
if ( value . isEmpty ( ) ) {
}
return
val filter = FilterCoordinator . require ( this )
}
when ( slider . id ) {
val selected = value . selectedItems . single ( )
R . id . slider _yearsRange -> filter . setYearRange (
b . spinnerOrder . adapter = ArrayAdapter (
valueFrom = slider . values . firstOrNull ( ) ?. let {
b . spinnerOrder . context ,
if ( it <= slider . valueFrom ) YEAR _UNKNOWN else it . toInt ( )
android . R . layout . simple _spinner _dropdown _item ,
} ?: YEAR _UNKNOWN ,
android . R . id . text1 ,
valueTo = slider . values . lastOrNull ( ) ?. let {
value . availableItems . map { b . spinnerOrder . context . getString ( it . titleRes ) } ,
if ( it >= slider . valueTo ) YEAR _UNKNOWN else it . toInt ( )
)
} ?: YEAR _UNKNOWN ,
val selectedIndex = value . availableItems . indexOf ( selected )
)
if ( selectedIndex >= 0 ) {
}
b . spinnerOrder . setSelection ( selectedIndex , false )
}
}
}
override fun onChipClick ( chip : Chip , data : Any ? ) {
val filter = FilterCoordinator . require ( this )
private fun onLocaleChanged ( value : FilterProperty < Locale ? > ) {
when ( data ) {
val b = viewBinding ?: return
is MangaState -> filter . toggleState ( data , ! chip . isChecked )
b . layoutLocale . isGone = value . isEmpty ( )
is MangaTag -> if ( chip . parentView ?. id == R . id . chips _genresExclude ) {
if ( value . isEmpty ( ) ) {
filter . toggleTagExclude ( data , ! chip . isChecked )
return
} else {
}
filter . toggleTag ( data , ! chip . isChecked )
val selected = value . selectedItems . singleOrNull ( )
}
b . spinnerLocale . adapter = ArrayAdapter (
b . spinnerLocale . context ,
is ContentType -> filter . toggleContentType ( data , ! chip . isChecked )
android . R . layout . simple _spinner _dropdown _item ,
is ContentRating -> filter . toggleContentRating ( data , ! chip . isChecked )
android . R . id . text1 ,
is Demographic -> filter . toggleDemographic ( data , ! chip . isChecked )
value . availableItems . map { it . getDisplayName ( b . spinnerLocale . context ) } ,
is PersistableFilter -> filter . setAdjusted ( data . filter )
)
is String -> if ( chip . isChecked ) {
val selectedIndex = value . availableItems . indexOf ( selected )
filter . setAuthor ( null )
if ( selectedIndex >= 0 ) {
} else {
b . spinnerLocale . setSelection ( selectedIndex , false )
filter . setAuthor ( data )
}
}
}
null -> router . showTagsCatalogSheet ( excludeMode = chip . parentView ?. id == R . id . chips _genresExclude )
}
private fun onOriginalLocaleChanged ( value : FilterProperty < Locale ? > ) {
}
val b = viewBinding ?: return
b . layoutOriginalLocale . isGone = value . isEmpty ( )
override fun onChipLongClick ( chip : Chip , data : Any ? ) : Boolean {
if ( value . isEmpty ( ) ) {
return when ( data ) {
return
is PersistableFilter -> {
}
showSavedFilterMenu ( chip , data )
val selected = value . selectedItems . singleOrNull ( )
true
b . spinnerOriginalLocale . adapter = ArrayAdapter (
}
b . spinnerOriginalLocale . context ,
android . R . layout . simple _spinner _dropdown _item ,
else -> false
android . R . id . text1 ,
}
value . availableItems . map { it . getDisplayName ( b . spinnerOriginalLocale . context ) } ,
}
)
val selectedIndex = value . availableItems . indexOf ( selected )
override fun onChipCloseClick ( chip : Chip , data : Any ? ) {
if ( selectedIndex >= 0 ) {
when ( data ) {
b . spinnerOriginalLocale . setSelection ( selectedIndex , false )
is PersistableFilter -> {
}
showSavedFilterMenu ( chip , data )
}
}
}
private fun onTagsChanged ( value : FilterProperty < MangaTag > ) {
}
val b = viewBinding ?: return
b . layoutGenres . isGone = value . isEmptyAndSuccess ( )
private fun onSortOrderChanged ( value : FilterProperty < SortOrder > ) {
b . layoutGenres . setError ( value . error ?. getDisplayMessage ( resources ) )
val b = viewBinding ?: return
if ( value . isEmpty ( ) ) {
b . layoutOrder . isGone = value . isEmpty ( )
return
if ( value . isEmpty ( ) ) {
}
return
val chips = value . availableItems . map { tag ->
}
ChipsView . ChipModel (
val selected = value . selectedItems . single ( )
title = tag . title ,
b . spinnerOrder . adapter = ArrayAdapter (
isChecked = tag in value . selectedItems ,
b . spinnerOrder . context ,
data = tag ,
android . R . layout . simple _spinner _dropdown _item ,
)
android . R . id . text1 ,
}
value . availableItems . map { b . spinnerOrder . context . getString ( it . titleRes ) } ,
b . chipsGenres . setChips ( chips )
)
}
val selectedIndex = value . availableItems . indexOf ( selected )
if ( selectedIndex >= 0 ) {
private fun onTagsExcludedChanged ( value : FilterProperty < MangaTag > ) {
b . spinnerOrder . setSelection ( selectedIndex , false )
val b = viewBinding ?: return
}
b . layoutGenresExclude . isGone = value . isEmpty ( )
}
if ( value . isEmpty ( ) ) {
return
private fun onLocaleChanged ( value : FilterProperty < Locale ? > ) {
}
val b = viewBinding ?: return
val chips = value . availableItems . map { tag ->
b . layoutLocale . isGone = value . isEmpty ( )
ChipsView . ChipModel (
if ( value . isEmpty ( ) ) {
title = tag . title ,
return
isChecked = tag in value . selectedItems ,
}
data = tag ,
val selected = value . selectedItems . singleOrNull ( )
)
b . spinnerLocale . adapter = ArrayAdapter (
}
b . spinnerLocale . context ,
b . chipsGenresExclude . setChips ( chips )
android . R . layout . simple _spinner _dropdown _item ,
}
android . R . id . text1 ,
value . availableItems . map { it . getDisplayName ( b . spinnerLocale . context ) } ,
private fun onStateChanged ( value : FilterProperty < MangaState > ) {
)
val b = viewBinding ?: return
val selectedIndex = value . availableItems . indexOf ( selected )
b . layoutState . isGone = value . isEmpty ( )
if ( selectedIndex >= 0 ) {
if ( value . isEmpty ( ) ) {
b . spinnerLocale . setSelection ( selectedIndex , false )
return
}
}
}
val chips = value . availableItems . map { state ->
ChipsView . ChipModel (
private fun onOriginalLocaleChanged ( value : FilterProperty < Locale ? > ) {
title = getString ( state . titleResId ) ,
val b = viewBinding ?: return
isChecked = state in value . selectedItems ,
b . layoutOriginalLocale . isGone = value . isEmpty ( )
data = state ,
if ( value . isEmpty ( ) ) {
)
return
}
}
b . chipsState . setChips ( chips )
val selected = value . selectedItems . singleOrNull ( )
}
b . spinnerOriginalLocale . adapter = ArrayAdapter (
b . spinnerOriginalLocale . context ,
private fun onContentTypesChanged ( value : FilterProperty < ContentType > ) {
android . R . layout . simple _spinner _dropdown _item ,
val b = viewBinding ?: return
android . R . id . text1 ,
b . layoutTypes . isGone = value . isEmpty ( )
value . availableItems . map { it . getDisplayName ( b . spinnerOriginalLocale . context ) } ,
if ( value . isEmpty ( ) ) {
)
return
val selectedIndex = value . availableItems . indexOf ( selected )
}
if ( selectedIndex >= 0 ) {
val chips = value . availableItems . map { type ->
b . spinnerOriginalLocale . setSelection ( selectedIndex , false )
ChipsView . ChipModel (
}
title = getString ( type . titleResId ) ,
}
isChecked = type in value . selectedItems ,
data = type ,
private fun onTagsChanged ( value : FilterProperty < MangaTag > ) {
)
val b = viewBinding ?: return
}
b . layoutGenres . isGone = value . isEmptyAndSuccess ( )
b . chipsTypes . setChips ( chips )
b . layoutGenres . setError ( value . error ?. getDisplayMessage ( resources ) )
}
if ( value . isEmpty ( ) ) {
return
private fun onContentRatingChanged ( value : FilterProperty < ContentRating > ) {
}
val b = viewBinding ?: return
val chips = value . availableItems . map { tag ->
b . layoutContentRating . isGone = value . isEmpty ( )
ChipsView . ChipModel (
if ( value . isEmpty ( ) ) {
title = tag . title ,
return
isChecked = tag in value . selectedItems ,
}
data = tag ,
val chips = value . availableItems . map { contentRating ->
)
ChipsView . ChipModel (
}
title = getString ( contentRating . titleResId ) ,
b . chipsGenres . setChips ( chips )
isChecked = contentRating in value . selectedItems ,
}
data = contentRating ,
)
private fun onTagsExcludedChanged ( value : FilterProperty < MangaTag > ) {
}
val b = viewBinding ?: return
b . chipsContentRating . setChips ( chips )
b . layoutGenresExclude . isGone = value . isEmpty ( )
}
if ( value . isEmpty ( ) ) {
return
private fun onDemographicsChanged ( value : FilterProperty < Demographic > ) {
}
val b = viewBinding ?: return
val chips = value . availableItems . map { tag ->
b . layoutDemographics . isGone = value . isEmpty ( )
ChipsView . ChipModel (
if ( value . isEmpty ( ) ) {
title = tag . title ,
return
isChecked = tag in value . selectedItems ,
}
data = tag ,
val chips = value . availableItems . map { demographic ->
)
ChipsView . ChipModel (
}
title = getString ( demographic . titleResId ) ,
b . chipsGenresExclude . setChips ( chips )
isChecked = demographic in value . selectedItems ,
}
data = demographic ,
)
private fun onAuthorsChanged ( value : FilterProperty < String > ) {
}
val b = viewBinding ?: return
b . chipsDemographics . setChips ( chips )
b . layoutAuthor . isGone = value . isEmpty ( )
}
if ( value . isEmpty ( ) ) {
return
private fun onYearChanged ( value : FilterProperty < Int > ) {
}
val b = viewBinding ?: return
val chips = value . availableItems . map { author ->
b . layoutYear . isGone = value . isEmpty ( )
ChipsView . ChipModel (
if ( value . isEmpty ( ) ) {
title = author ,
return
isChecked = author in value . selectedItems ,
}
data = author ,
val currentValue = value . selectedItems . singleOrNull ( ) ?: YEAR _UNKNOWN
)
b . layoutYear . setValueText (
}
if ( currentValue == YEAR _UNKNOWN ) {
b . chipsAuthor . setChips ( chips )
getString ( R . string . any )
}
} else {
currentValue . toString ( )
private fun onStateChanged ( value : FilterProperty < MangaState > ) {
} ,
val b = viewBinding ?: return
)
b . layoutState . isGone = value . isEmpty ( )
b . sliderYear . valueFrom = value . availableItems . first ( ) . toFloat ( )
if ( value . isEmpty ( ) ) {
b . sliderYear . valueTo = value . availableItems . last ( ) . toFloat ( )
return
b . sliderYear . setValueRounded ( currentValue . toFloat ( ) )
}
}
val chips = value . availableItems . map { state ->
ChipsView . ChipModel (
private fun onYearRangeChanged ( value : FilterProperty < Int > ) {
title = getString ( state . titleResId ) ,
val b = viewBinding ?: return
isChecked = state in value . selectedItems ,
b . layoutYearsRange . isGone = value . isEmpty ( )
data = state ,
if ( value . isEmpty ( ) ) {
)
return
}
}
b . chipsState . setChips ( chips )
b . sliderYearsRange . valueFrom = value . availableItems . first ( ) . toFloat ( )
}
b . sliderYearsRange . valueTo = value . availableItems . last ( ) . toFloat ( )
val currentValueFrom = value . selectedItems . firstOrNull ( ) ?. toFloat ( ) ?: b . sliderYearsRange . valueFrom
private fun onContentTypesChanged ( value : FilterProperty < ContentType > ) {
val currentValueTo = value . selectedItems . lastOrNull ( ) ?. toFloat ( ) ?: b . sliderYearsRange . valueTo
val b = viewBinding ?: return
b . layoutYearsRange . setValueText (
b . layoutTypes . isGone = value . isEmpty ( )
getString (
if ( value . isEmpty ( ) ) {
R . string . memory _usage _pattern ,
return
currentValueFrom . toInt ( ) . toString ( ) ,
}
currentValueTo . toInt ( ) . toString ( ) ,
val chips = value . availableItems . map { type ->
) ,
ChipsView . ChipModel (
)
title = getString ( type . titleResId ) ,
b . sliderYearsRange . setValuesRounded ( currentValueFrom , currentValueTo )
isChecked = type in value . selectedItems ,
}
data = type ,
)
}
b . chipsTypes . setChips ( chips )
}
private fun onContentRatingChanged ( value : FilterProperty < ContentRating > ) {
val b = viewBinding ?: return
b . layoutContentRating . isGone = value . isEmpty ( )
if ( value . isEmpty ( ) ) {
return
}
val chips = value . availableItems . map { contentRating ->
ChipsView . ChipModel (
title = getString ( contentRating . titleResId ) ,
isChecked = contentRating in value . selectedItems ,
data = contentRating ,
)
}
b . chipsContentRating . setChips ( chips )
}
private fun onDemographicsChanged ( value : FilterProperty < Demographic > ) {
val b = viewBinding ?: return
b . layoutDemographics . isGone = value . isEmpty ( )
if ( value . isEmpty ( ) ) {
return
}
val chips = value . availableItems . map { demographic ->
ChipsView . ChipModel (
title = getString ( demographic . titleResId ) ,
isChecked = demographic in value . selectedItems ,
data = demographic ,
)
}
b . chipsDemographics . setChips ( chips )
}
private fun onYearChanged ( value : FilterProperty < Int > ) {
val b = viewBinding ?: return
b . layoutYear . isGone = value . isEmpty ( )
if ( value . isEmpty ( ) ) {
return
}
val currentValue = value . selectedItems . singleOrNull ( ) ?: YEAR _UNKNOWN
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 ( ) )
}
private fun onYearRangeChanged ( value : FilterProperty < Int > ) {
val b = viewBinding ?: return
b . layoutYearsRange . isGone = value . isEmpty ( )
if ( value . isEmpty ( ) ) {
return
}
b . sliderYearsRange . valueFrom = value . availableItems . first ( ) . toFloat ( )
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 . layoutYearsRange . setValueText (
getString (
R . string . memory _usage _pattern ,
currentValueFrom . toInt ( ) . toString ( ) ,
currentValueTo . toInt ( ) . toString ( ) ,
) ,
)
b . sliderYearsRange . setValuesRounded ( currentValueFrom , currentValueTo )
}
private fun onSavedPresetsChanged ( value : FilterProperty < PersistableFilter > ) {
val b = viewBinding ?: return
b . layoutSavedFilters . isGone = value . isEmpty ( )
if ( value . isEmpty ( ) ) {
return
}
val chips = value . availableItems . map { f ->
ChipsView . ChipModel (
title = f . name ,
isChecked = f in value . selectedItems ,
data = f ,
isDropdown = true ,
)
}
b . chipsSavedFilters . setChips ( chips )
}
private fun showSavedFilterMenu ( anchor : View , preset : PersistableFilter ) {
val menu = PopupMenu ( context ?: return , anchor )
val filter = FilterCoordinator . require ( this )
menu . inflate ( R . menu . popup _saved _filter )
menu . setOnMenuItemClickListener { menuItem ->
when ( menuItem . itemId ) {
R . id . action _delete -> filter . deleteSavedFilter ( preset . id )
R . id . action _rename -> onRenameFilterClick ( preset )
}
true
}
menu . show ( )
}
private fun onSaveFilterClick ( name : String ) {
val filter = FilterCoordinator . require ( this )
val existingNames = filter . savedFilters . value . availableItems
. mapTo ( TreeSet ( AlphanumComparator ( ) ) , PersistableFilter :: name )
buildAlertDialog ( context ?: return ) {
val input = setEditText (
entries = existingNames . toList ( ) ,
inputType = EditorInfo . TYPE _CLASS _TEXT or EditorInfo . TYPE _TEXT _FLAG _CAP _SENTENCES ,
singleLine = true ,
)
input . setHint ( R . string . enter _name )
input . setText ( name )
input . filters += InputFilter . LengthFilter ( MAX _TITLE _LENGTH )
setTitle ( R . string . save _filter )
setPositiveButton ( R . string . save ) { _ , _ ->
val text = input . text ?. toString ( ) ?. trim ( )
if ( text . isNullOrEmpty ( ) ) {
Toast . makeText ( context , R . string . invalid _value _message , Toast . LENGTH _SHORT ) . show ( )
onSaveFilterClick ( " " )
} else if ( text in existingNames ) {
askForFilterOverwrite ( filter , text )
} else {
filter . saveCurrentFilter ( text )
}
}
setNegativeButton ( android . R . string . cancel , null )
} . show ( )
}
private fun onRenameFilterClick ( preset : PersistableFilter ) {
val filter = FilterCoordinator . require ( this )
val existingNames = filter . savedFilters . value . availableItems . mapToSet { it . name }
buildAlertDialog ( context ?: return ) {
val input = setEditText (
inputType = EditorInfo . TYPE _CLASS _TEXT or EditorInfo . TYPE _TEXT _FLAG _CAP _SENTENCES ,
singleLine = true ,
)
input . filters += InputFilter . LengthFilter ( MAX _TITLE _LENGTH )
input . setHint ( R . string . enter _name )
input . setText ( preset . name )
setTitle ( R . string . rename )
setPositiveButton ( R . string . save ) { _ , _ ->
val text = input . text ?. toString ( ) ?. trim ( )
if ( text . isNullOrEmpty ( ) || text in existingNames ) {
Toast . makeText ( context , R . string . invalid _value _message , Toast . LENGTH _SHORT ) . show ( )
} else {
filter . renameSavedFilter ( preset . id , text )
}
}
setNegativeButton ( android . R . string . cancel , null )
} . show ( )
}
private fun askForFilterOverwrite ( filter : FilterCoordinator , name : String ) {
buildAlertDialog ( context ?: return ) {
setTitle ( R . string . save _filter )
setMessage ( getString ( R . string . filter _overwrite _confirm , name ) )
setPositiveButton ( R . string . overwrite ) { _ , _ ->
filter . saveCurrentFilter ( name )
}
setNegativeButton ( android . R . string . cancel ) { _ , _ ->
onSaveFilterClick ( name )
}
} . show ( )
}
}
}