diff --git a/app/build.gradle b/app/build.gradle index 8127b8af1..a62808d96 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -83,7 +83,7 @@ afterEvaluate { } dependencies { //noinspection GradleDependency - implementation('com.github.KotatsuApp:kotatsu-parsers:2e138da3d5') { + implementation('com.github.KotatsuApp:kotatsu-parsers:7528480f54') { exclude group: 'org.json', module: 'json' } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/GenericSortOrder.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/GenericSortOrder.kt new file mode 100644 index 000000000..34d5a169b --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/GenericSortOrder.kt @@ -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 + } + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/SortDirection.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/SortDirection.kt new file mode 100644 index 000000000..f64eeaa53 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/SortDirection.kt @@ -0,0 +1,6 @@ +package org.koitharu.kotatsu.core.model + +enum class SortDirection { + + ASC, DESC; +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginContentSource.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginContentSource.kt index 960bbdd2c..12ae5fe2e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginContentSource.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginContentSource.kt @@ -31,7 +31,7 @@ class ExternalPluginContentSource( @Blocking @WorkerThread - fun getList(offset: Int, filter: MangaListFilter?): List = runCatchingCompatibility { + fun getList(offset: Int, filter: MangaListFilter?): List { 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(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 = runCatchingCompatibility { + fun getPages(chapter: MangaChapter): List { 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(cursor.count) @@ -113,9 +113,9 @@ class ExternalPluginContentSource( @Blocking @WorkerThread - fun getTags(): Set = runCatchingCompatibility { + fun getTags(): Set { 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(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 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, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/SafeCursor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginCursor.kt similarity index 63% rename from app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/SafeCursor.kt rename to app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginCursor.kt index e7ac48455..33cab9511 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/SafeCursor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/external/ExternalPluginCursor.kt @@ -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) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/model/SortOrder.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/model/SortOrder.kt index da1f262c8..e324b1a4a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/model/SortOrder.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/model/SortOrder.kt @@ -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 } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterCoordinator.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterCoordinator.kt index bd5ea3c17..64ae0bd29 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterCoordinator.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterCoordinator.kt @@ -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> = 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> = + 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> = + 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> = 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, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/MangaFilter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/MangaFilter.kt index 26fcf6fab..11dfcef1a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/MangaFilter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/MangaFilter.kt @@ -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> - val filterSortOrder: StateFlow> + val filterSortOrder: StateFlow> + + val filterSortDirection: StateFlow> val filterState: StateFlow> diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt index 8e210d988..1c9cc6401 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/sheet/FilterSheetFragment.kt @@ -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(), 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(), } 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(), 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(), } } - private fun onSortOrderChanged(value: FilterProperty) { + private fun onSortOrderChanged(value: FilterProperty) { val b = viewBinding ?: return b.textViewOrderTitle.isGone = value.isEmpty() b.cardOrder.isGone = value.isEmpty() @@ -102,7 +122,7 @@ class FilterSheetFragment : BaseAdaptiveSheet(), 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(), } } + private fun onSortDirectionChanged(value: FilterProperty) { + 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) { val b = viewBinding ?: return b.textViewLocaleTitle.isGone = value.isEmpty() @@ -239,6 +278,19 @@ class FilterSheetFragment : BaseAdaptiveSheet(), 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" diff --git a/app/src/main/res/drawable/ic_sort_asc.xml b/app/src/main/res/drawable/ic_sort_asc.xml new file mode 100644 index 000000000..f19ddac65 --- /dev/null +++ b/app/src/main/res/drawable/ic_sort_asc.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_sort_desc.xml b/app/src/main/res/drawable/ic_sort_desc.xml new file mode 100644 index 000000000..d9f9cda6a --- /dev/null +++ b/app/src/main/res/drawable/ic_sort_desc.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/sheet_filter.xml b/app/src/main/res/layout/sheet_filter.xml index 3c9e2836d..27f1494ac 100644 --- a/app/src/main/res/layout/sheet_filter.xml +++ b/app/src/main/res/layout/sheet_filter.xml @@ -39,6 +39,7 @@ + + + + + + + + + android:minHeight="?listPreferredItemHeightSmall" + android:paddingHorizontal="8dp" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd905d89c..e1237015b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -686,4 +686,11 @@ Skip all Stuck Not in favoites + Updated long ago + Unpopular + Low rating + Ascending + Descending + Date + Popularity