Support ascending/descending sort orders
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
package org.koitharu.kotatsu.core.model
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
|
||||
enum class GenericSortOrder(
|
||||
@StringRes val titleResId: Int,
|
||||
val ascending: SortOrder,
|
||||
val descending: SortOrder,
|
||||
) {
|
||||
|
||||
UPDATED(R.string.updated, SortOrder.UPDATED_ASC, SortOrder.UPDATED),
|
||||
RATING(R.string.by_rating, SortOrder.RATING_ASC, SortOrder.RATING),
|
||||
POPULARITY(R.string.popularity, SortOrder.POPULARITY_ASC, SortOrder.POPULARITY),
|
||||
DATE(R.string.by_date, SortOrder.NEWEST_ASC, SortOrder.NEWEST),
|
||||
NAME(R.string.by_name, SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL_DESC),
|
||||
;
|
||||
|
||||
operator fun get(direction: SortDirection): SortOrder = when (direction) {
|
||||
SortDirection.ASC -> ascending
|
||||
SortDirection.DESC -> descending
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun of(order: SortOrder): GenericSortOrder = entries.first { e ->
|
||||
e.ascending == order || e.descending == order
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.koitharu.kotatsu.core.model
|
||||
|
||||
enum class SortDirection {
|
||||
|
||||
ASC, DESC;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ class ExternalPluginContentSource(
|
||||
|
||||
@Blocking
|
||||
@WorkerThread
|
||||
fun getList(offset: Int, filter: MangaListFilter?): List<Manga> = runCatchingCompatibility {
|
||||
fun getList(offset: Int, filter: MangaListFilter?): List<Manga> {
|
||||
val uri = "content://${source.authority}/manga".toUri().buildUpon()
|
||||
uri.appendQueryParameter("offset", offset.toString())
|
||||
when (filter) {
|
||||
@@ -49,7 +49,7 @@ class ExternalPluginContentSource(
|
||||
|
||||
null -> Unit
|
||||
}
|
||||
contentResolver.query(uri.build(), null, null, null, filter?.sortOrder?.name)
|
||||
return contentResolver.query(uri.build(), null, null, null, filter?.sortOrder?.name)
|
||||
.safe()
|
||||
.use { cursor ->
|
||||
val result = ArrayList<Manga>(cursor.count)
|
||||
@@ -64,10 +64,10 @@ class ExternalPluginContentSource(
|
||||
|
||||
@Blocking
|
||||
@WorkerThread
|
||||
fun getDetails(manga: Manga) = runCatchingCompatibility {
|
||||
fun getDetails(manga: Manga): Manga {
|
||||
val chapters = queryChapters(manga.url)
|
||||
val details = queryDetails(manga.url)
|
||||
Manga(
|
||||
return Manga(
|
||||
id = manga.id,
|
||||
title = details.title.ifBlank { manga.title },
|
||||
altTitle = details.altTitle.ifNullOrEmpty { manga.altTitle },
|
||||
@@ -88,12 +88,12 @@ class ExternalPluginContentSource(
|
||||
|
||||
@Blocking
|
||||
@WorkerThread
|
||||
fun getPages(chapter: MangaChapter): List<MangaPage> = runCatchingCompatibility {
|
||||
fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
val uri = "content://${source.authority}/chapters".toUri()
|
||||
.buildUpon()
|
||||
.appendPath(chapter.url)
|
||||
.build()
|
||||
contentResolver.query(uri, null, null, null, null)
|
||||
return contentResolver.query(uri, null, null, null, null)
|
||||
.safe()
|
||||
.use { cursor ->
|
||||
val result = ArrayList<MangaPage>(cursor.count)
|
||||
@@ -113,9 +113,9 @@ class ExternalPluginContentSource(
|
||||
|
||||
@Blocking
|
||||
@WorkerThread
|
||||
fun getTags(): Set<MangaTag> = runCatchingCompatibility {
|
||||
fun getTags(): Set<MangaTag> {
|
||||
val uri = "content://${source.authority}/tags".toUri()
|
||||
contentResolver.query(uri, null, null, null, null)
|
||||
return contentResolver.query(uri, null, null, null, null)
|
||||
.safe()
|
||||
.use { cursor ->
|
||||
val result = ArraySet<MangaTag>(cursor.count)
|
||||
@@ -212,7 +212,7 @@ class ExternalPluginContentSource(
|
||||
}
|
||||
}
|
||||
|
||||
private fun SafeCursor.getManga() = Manga(
|
||||
private fun ExternalPluginCursor.getManga() = Manga(
|
||||
id = getLong(COLUMN_ID),
|
||||
title = getString(COLUMN_TITLE),
|
||||
altTitle = getStringOrNull(COLUMN_ALT_TITLE),
|
||||
@@ -233,15 +233,10 @@ class ExternalPluginContentSource(
|
||||
source = source,
|
||||
)
|
||||
|
||||
private inline fun <R> runCatchingCompatibility(block: () -> R): R = try {
|
||||
block()
|
||||
} catch (e: NoSuchElementException) { // unknown column name
|
||||
throw IncompatiblePluginException(source.name, e)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw IncompatiblePluginException(source.name, e)
|
||||
}
|
||||
|
||||
private fun Cursor?.safe() = SafeCursor(this ?: throw IncompatiblePluginException(source.name, null))
|
||||
private fun Cursor?.safe() = ExternalPluginCursor(
|
||||
source = source,
|
||||
cursor = this ?: throw IncompatiblePluginException(source.name, null),
|
||||
)
|
||||
|
||||
class MangaSourceCapabilities(
|
||||
val availableSortOrders: Set<SortOrder>,
|
||||
|
||||
@@ -2,14 +2,19 @@ package org.koitharu.kotatsu.core.parser.external
|
||||
|
||||
import android.database.Cursor
|
||||
import android.database.CursorWrapper
|
||||
import org.koitharu.kotatsu.core.exceptions.IncompatiblePluginException
|
||||
import org.koitharu.kotatsu.core.util.ext.getBoolean
|
||||
|
||||
class SafeCursor(cursor: Cursor) : CursorWrapper(cursor) {
|
||||
class ExternalPluginCursor(private val source: ExternalMangaSource, cursor: Cursor) : CursorWrapper(cursor) {
|
||||
|
||||
fun getString(columnName: String): String {
|
||||
return getString(getColumnIndexOrThrow(columnName))
|
||||
override fun getColumnIndexOrThrow(columnName: String?): Int = try {
|
||||
super.getColumnIndexOrThrow(columnName)
|
||||
} catch (e: Exception) {
|
||||
throw IncompatiblePluginException(source.name, e)
|
||||
}
|
||||
|
||||
fun getString(columnName: String): String = getString(getColumnIndexOrThrow(columnName))
|
||||
|
||||
fun getStringOrNull(columnName: String): String? {
|
||||
val columnIndex = getColumnIndex(columnName)
|
||||
return when {
|
||||
@@ -19,9 +24,7 @@ class SafeCursor(cursor: Cursor) : CursorWrapper(cursor) {
|
||||
}
|
||||
}
|
||||
|
||||
fun getBoolean(columnName: String): Boolean {
|
||||
return getBoolean(getColumnIndexOrThrow(columnName))
|
||||
}
|
||||
fun getBoolean(columnName: String): Boolean = getBoolean(getColumnIndexOrThrow(columnName))
|
||||
|
||||
fun getBooleanOrDefault(columnName: String, defaultValue: Boolean): Boolean {
|
||||
val columnIndex = getColumnIndex(columnName)
|
||||
@@ -32,9 +35,7 @@ class SafeCursor(cursor: Cursor) : CursorWrapper(cursor) {
|
||||
}
|
||||
}
|
||||
|
||||
fun getInt(columnName: String): Int {
|
||||
return getInt(getColumnIndexOrThrow(columnName))
|
||||
}
|
||||
fun getInt(columnName: String): Int = getInt(getColumnIndexOrThrow(columnName))
|
||||
|
||||
fun getIntOrDefault(columnName: String, defaultValue: Int): Int {
|
||||
val columnIndex = getColumnIndex(columnName)
|
||||
@@ -45,9 +46,7 @@ class SafeCursor(cursor: Cursor) : CursorWrapper(cursor) {
|
||||
}
|
||||
}
|
||||
|
||||
fun getLong(columnName: String): Long {
|
||||
return getLong(getColumnIndexOrThrow(columnName))
|
||||
}
|
||||
fun getLong(columnName: String): Long = getLong(getColumnIndexOrThrow(columnName))
|
||||
|
||||
fun getLongOrDefault(columnName: String, defaultValue: Long): Long {
|
||||
val columnIndex = getColumnIndex(columnName)
|
||||
@@ -58,9 +57,7 @@ class SafeCursor(cursor: Cursor) : CursorWrapper(cursor) {
|
||||
}
|
||||
}
|
||||
|
||||
fun getFloat(columnName: String): Float {
|
||||
return getFloat(getColumnIndexOrThrow(columnName))
|
||||
}
|
||||
fun getFloat(columnName: String): Float = getFloat(getColumnIndexOrThrow(columnName))
|
||||
|
||||
fun getFloatOrDefault(columnName: String, defaultValue: Float): Float {
|
||||
val columnIndex = getColumnIndex(columnName)
|
||||
@@ -2,15 +2,45 @@ package org.koitharu.kotatsu.core.ui.model
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.SortDirection
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.ALPHABETICAL
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.ALPHABETICAL_DESC
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.NEWEST
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.NEWEST_ASC
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.POPULARITY
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.POPULARITY_ASC
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.RATING
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.RATING_ASC
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.UPDATED
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder.UPDATED_ASC
|
||||
|
||||
@get:StringRes
|
||||
val SortOrder.titleRes: Int
|
||||
get() = when (this) {
|
||||
SortOrder.UPDATED -> R.string.updated
|
||||
SortOrder.POPULARITY -> R.string.popular
|
||||
SortOrder.RATING -> R.string.by_rating
|
||||
SortOrder.NEWEST -> R.string.newest
|
||||
SortOrder.ALPHABETICAL -> R.string.by_name
|
||||
SortOrder.ALPHABETICAL_DESC -> R.string.by_name_reverse
|
||||
UPDATED -> R.string.updated
|
||||
POPULARITY -> R.string.popular
|
||||
RATING -> R.string.by_rating
|
||||
NEWEST -> R.string.newest
|
||||
ALPHABETICAL -> R.string.by_name
|
||||
ALPHABETICAL_DESC -> R.string.by_name_reverse
|
||||
UPDATED_ASC -> R.string.updated_long_ago
|
||||
POPULARITY_ASC -> R.string.unpopular
|
||||
RATING_ASC -> R.string.low_rating
|
||||
NEWEST_ASC -> R.string.order_oldest
|
||||
}
|
||||
|
||||
val SortOrder.direction: SortDirection
|
||||
get() = when (this) {
|
||||
UPDATED_ASC,
|
||||
POPULARITY_ASC,
|
||||
RATING_ASC,
|
||||
NEWEST_ASC,
|
||||
ALPHABETICAL -> SortDirection.ASC
|
||||
|
||||
UPDATED,
|
||||
POPULARITY,
|
||||
RATING,
|
||||
NEWEST,
|
||||
ALPHABETICAL_DESC -> SortDirection.DESC
|
||||
}
|
||||
|
||||
@@ -24,14 +24,18 @@ import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.GenericSortOrder
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.model.SortDirection
|
||||
import org.koitharu.kotatsu.core.parser.MangaDataRepository
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.ui.model.direction
|
||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.core.util.LocaleComparator
|
||||
import org.koitharu.kotatsu.core.util.ext.asArrayList
|
||||
import org.koitharu.kotatsu.core.util.ext.lifecycleScope
|
||||
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.core.util.ext.sortedByOrdinal
|
||||
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel
|
||||
import org.koitharu.kotatsu.filter.ui.model.FilterProperty
|
||||
import org.koitharu.kotatsu.filter.ui.model.TagCatalogItem
|
||||
@@ -52,6 +56,7 @@ import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment
|
||||
import org.koitharu.kotatsu.search.domain.MangaSearchRepository
|
||||
import java.text.Collator
|
||||
import java.util.EnumSet
|
||||
import java.util.LinkedList
|
||||
import java.util.Locale
|
||||
import java.util.TreeSet
|
||||
@@ -131,24 +136,42 @@ class FilterCoordinator @Inject constructor(
|
||||
MutableStateFlow(emptyProperty())
|
||||
}
|
||||
|
||||
override val filterSortOrder: StateFlow<FilterProperty<SortOrder>> = combine(
|
||||
currentState.distinctUntilChangedBy { it.sortOrder },
|
||||
flowOf(repository.sortOrders),
|
||||
) { state, orders ->
|
||||
FilterProperty(
|
||||
availableItems = orders.sortedBy { it.ordinal },
|
||||
selectedItems = setOf(state.sortOrder),
|
||||
isLoading = false,
|
||||
error = null,
|
||||
)
|
||||
}.stateIn(coroutineScope + Dispatchers.Default, SharingStarted.Lazily, loadingProperty())
|
||||
override val filterSortOrder: StateFlow<FilterProperty<GenericSortOrder>> =
|
||||
currentState.distinctUntilChangedBy { it.sortOrder }.map { state ->
|
||||
val orders = repository.sortOrders
|
||||
FilterProperty(
|
||||
availableItems = orders.mapTo(EnumSet.noneOf(GenericSortOrder::class.java)) {
|
||||
GenericSortOrder.of(it)
|
||||
}.sortedByOrdinal(),
|
||||
selectedItems = setOf(GenericSortOrder.of(state.sortOrder)),
|
||||
isLoading = false,
|
||||
error = null,
|
||||
)
|
||||
}.stateIn(coroutineScope + Dispatchers.Default, SharingStarted.Lazily, loadingProperty())
|
||||
|
||||
override val filterSortDirection: StateFlow<FilterProperty<SortDirection>> =
|
||||
currentState.distinctUntilChangedBy { it.sortOrder }.map { state ->
|
||||
val orders = repository.sortOrders
|
||||
FilterProperty(
|
||||
availableItems = state.sortOrder.let {
|
||||
val genericOrder = GenericSortOrder.of(it)
|
||||
val result = EnumSet.noneOf(SortDirection::class.java)
|
||||
if (genericOrder.ascending in orders) result.add(SortDirection.ASC)
|
||||
if (genericOrder.descending in orders) result.add(SortDirection.DESC)
|
||||
result
|
||||
}?.sortedByOrdinal().orEmpty(),
|
||||
selectedItems = setOf(state.sortOrder.direction),
|
||||
isLoading = false,
|
||||
error = null,
|
||||
)
|
||||
}.stateIn(coroutineScope + Dispatchers.Default, SharingStarted.Lazily, loadingProperty())
|
||||
|
||||
override val filterState: StateFlow<FilterProperty<MangaState>> = combine(
|
||||
currentState.distinctUntilChangedBy { it.states },
|
||||
flowOf(repository.states),
|
||||
) { state, states ->
|
||||
FilterProperty(
|
||||
availableItems = states.sortedBy { it.ordinal },
|
||||
availableItems = states.sortedByOrdinal(),
|
||||
selectedItems = state.states,
|
||||
isLoading = false,
|
||||
error = null,
|
||||
@@ -160,7 +183,7 @@ class FilterCoordinator @Inject constructor(
|
||||
flowOf(repository.contentRatings),
|
||||
) { rating, ratings ->
|
||||
FilterProperty(
|
||||
availableItems = ratings.sortedBy { it.ordinal },
|
||||
availableItems = ratings.sortedByOrdinal(),
|
||||
selectedItems = rating.contentRating,
|
||||
isLoading = false,
|
||||
error = null,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package org.koitharu.kotatsu.filter.ui
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.koitharu.kotatsu.core.model.GenericSortOrder
|
||||
import org.koitharu.kotatsu.core.model.SortDirection
|
||||
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel
|
||||
import org.koitharu.kotatsu.filter.ui.model.FilterProperty
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.ContentRating
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import java.util.Locale
|
||||
|
||||
interface MangaFilter : OnFilterChangedListener {
|
||||
@@ -18,7 +19,9 @@ interface MangaFilter : OnFilterChangedListener {
|
||||
|
||||
val filterTagsExcluded: StateFlow<FilterProperty<MangaTag>>
|
||||
|
||||
val filterSortOrder: StateFlow<FilterProperty<SortOrder>>
|
||||
val filterSortOrder: StateFlow<FilterProperty<GenericSortOrder>>
|
||||
|
||||
val filterSortDirection: StateFlow<FilterProperty<SortDirection>>
|
||||
|
||||
val filterState: StateFlow<FilterProperty<MangaState>>
|
||||
|
||||
|
||||
@@ -7,13 +7,17 @@ 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 org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.GenericSortOrder
|
||||
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.model.direction
|
||||
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
|
||||
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
@@ -21,6 +25,7 @@ import org.koitharu.kotatsu.core.util.ext.getDisplayName
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.parentView
|
||||
import org.koitharu.kotatsu.core.util.ext.showDistinct
|
||||
import org.koitharu.kotatsu.core.util.ext.sortedByOrdinal
|
||||
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
||||
import org.koitharu.kotatsu.databinding.SheetFilterBinding
|
||||
import org.koitharu.kotatsu.filter.ui.FilterOwner
|
||||
@@ -30,12 +35,14 @@ import org.koitharu.kotatsu.parsers.model.ContentRating
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import java.util.EnumSet
|
||||
import java.util.Locale
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
|
||||
AdapterView.OnItemSelectedListener,
|
||||
ChipsView.OnChipClickListener {
|
||||
ChipsView.OnChipClickListener, MaterialButtonToggleGroup.OnButtonCheckedListener {
|
||||
|
||||
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetFilterBinding {
|
||||
return SheetFilterBinding.inflate(inflater, container, false)
|
||||
@@ -51,6 +58,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
|
||||
}
|
||||
val filter = requireFilter()
|
||||
filter.filterSortOrder.observe(viewLifecycleOwner, this::onSortOrderChanged)
|
||||
filter.filterSortDirection.observe(viewLifecycleOwner, this::onSortDirectionChanged)
|
||||
filter.filterLocale.observe(viewLifecycleOwner, this::onLocaleChanged)
|
||||
filter.filterTags.observe(viewLifecycleOwner, this::onTagsChanged)
|
||||
filter.filterTagsExcluded.observe(viewLifecycleOwner, this::onTagsExcludedChanged)
|
||||
@@ -63,12 +71,24 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
|
||||
binding.chipsContentRating.onChipClickListener = this
|
||||
binding.chipsGenres.onChipClickListener = this
|
||||
binding.chipsGenresExclude.onChipClickListener = this
|
||||
binding.layoutSortDirection.addOnButtonCheckedListener(this)
|
||||
}
|
||||
|
||||
override fun onButtonChecked(group: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean) {
|
||||
if (isChecked) {
|
||||
setSortDirection(getSortDirection(checkedId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
|
||||
val filter = requireFilter()
|
||||
when (parent.id) {
|
||||
R.id.spinner_order -> filter.setSortOrder(filter.filterSortOrder.value.availableItems[position])
|
||||
R.id.spinner_order -> {
|
||||
val genericOrder = filter.filterSortOrder.value.availableItems[position]
|
||||
val direction = getSortDirection(requireViewBinding().layoutSortDirection.checkedButtonId)
|
||||
filter.setSortOrder(genericOrder[direction])
|
||||
}
|
||||
|
||||
R.id.spinner_locale -> filter.setLanguage(filter.filterLocale.value.availableItems[position])
|
||||
}
|
||||
}
|
||||
@@ -90,7 +110,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSortOrderChanged(value: FilterProperty<SortOrder>) {
|
||||
private fun onSortOrderChanged(value: FilterProperty<GenericSortOrder>) {
|
||||
val b = viewBinding ?: return
|
||||
b.textViewOrderTitle.isGone = value.isEmpty()
|
||||
b.cardOrder.isGone = value.isEmpty()
|
||||
@@ -102,7 +122,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
|
||||
b.spinnerOrder.context,
|
||||
android.R.layout.simple_spinner_dropdown_item,
|
||||
android.R.id.text1,
|
||||
value.availableItems.map { b.spinnerOrder.context.getString(it.titleRes) },
|
||||
value.availableItems.map { b.spinnerOrder.context.getString(it.titleResId) },
|
||||
)
|
||||
val selectedIndex = value.availableItems.indexOf(selected)
|
||||
if (selectedIndex >= 0) {
|
||||
@@ -110,6 +130,25 @@ 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()
|
||||
@@ -239,6 +278,19 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
|
||||
|
||||
private fun requireFilter() = (requireActivity() as FilterOwner).filter
|
||||
|
||||
private fun setSortDirection(direction: SortDirection) {
|
||||
val filter = requireFilter()
|
||||
val currentOrder = filter.filterSortOrder.value.selectedItems.single()
|
||||
val newOrder = currentOrder[direction]
|
||||
filter.setSortOrder(newOrder)
|
||||
}
|
||||
|
||||
private fun getSortDirection(@IdRes buttonId: Int): SortDirection = when (buttonId) {
|
||||
R.id.button_order_asc -> SortDirection.ASC
|
||||
R.id.button_order_desc -> SortDirection.DESC
|
||||
else -> throw IllegalArgumentException("Wrong button id $buttonId")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "FilterSheet"
|
||||
|
||||
12
app/src/main/res/drawable/ic_sort_asc.xml
Normal file
12
app/src/main/res/drawable/ic_sort_asc.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M19 17H22L18 21L14 17H17V3H19M2 17H12V19H2M6 5V7H2V5M2 11H9V13H2V11Z" />
|
||||
</vector>
|
||||
12
app/src/main/res/drawable/ic_sort_desc.xml
Normal file
12
app/src/main/res/drawable/ic_sort_desc.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M19 7H22L18 3L14 7H17V21H19M2 17H12V19H2M6 5V7H2V5M2 11H9V13H2V11Z" />
|
||||
</vector>
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
<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"
|
||||
@@ -54,6 +55,37 @@
|
||||
|
||||
</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:weightSum="2"
|
||||
app:selectionRequired="true"
|
||||
app:singleSelection="true">
|
||||
|
||||
<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_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/sort_order_desc"
|
||||
app:icon="@drawable/ic_sort_desc" />
|
||||
|
||||
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_locale_title"
|
||||
android:layout_width="match_parent"
|
||||
@@ -67,6 +99,7 @@
|
||||
|
||||
<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"
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
|
||||
<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_marginHorizontal="16dp"
|
||||
@@ -124,8 +125,8 @@
|
||||
android:id="@+id/spinner_order"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:minHeight="?listPreferredItemHeightSmall" />
|
||||
android:minHeight="?listPreferredItemHeightSmall"
|
||||
android:paddingHorizontal="8dp" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
||||
@@ -686,4 +686,11 @@
|
||||
<string name="skip_all">Skip all</string>
|
||||
<string name="stuck">Stuck</string>
|
||||
<string name="not_in_favorites">Not in favoites</string>
|
||||
<string name="updated_long_ago">Updated long ago</string>
|
||||
<string name="unpopular">Unpopular</string>
|
||||
<string name="low_rating">Low rating</string>
|
||||
<string name="sort_order_asc">Ascending</string>
|
||||
<string name="sort_order_desc">Descending</string>
|
||||
<string name="by_date">Date</string>
|
||||
<string name="popularity">Popularity</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user