From 92132c59f5417ef81a7bbba6849be849282fc25e Mon Sep 17 00:00:00 2001 From: arkon Date: Thu, 23 Feb 2023 22:32:40 -0500 Subject: [PATCH] Migrate source filter sheet to Compose (#9135) --- .../presentation/components/SettingsItems.kt | 157 ++++++----------- .../library/LibrarySettingsDialog.kt | 8 +- .../manga/ChapterSettingsDialog.kt | 4 +- .../migration/search/SourceSearchScreen.kt | 7 - .../source/browse/BrowseSourceScreen.kt | 23 ++- .../source/browse/BrowseSourceScreenModel.kt | 89 ++-------- .../source/browse/SourceFilterDialog.kt | 165 ++++++++++++++++++ .../browse/source/browse/SourceFilterSheet.kt | 63 ------- .../ui/browse/source/filter/CheckboxItem.kt | 46 ----- .../ui/browse/source/filter/GroupItem.kt | 66 ------- .../ui/browse/source/filter/HeaderItem.kt | 41 ----- .../ui/browse/source/filter/SectionItems.kt | 100 ----------- .../ui/browse/source/filter/SelectItem.kt | 59 ------- .../ui/browse/source/filter/SeparatorItem.kt | 38 ---- .../ui/browse/source/filter/SortGroup.kt | 56 ------ .../ui/browse/source/filter/SortItem.kt | 75 -------- .../ui/browse/source/filter/TextItem.kt | 47 ----- .../ui/browse/source/filter/TriStateItem.kt | 80 --------- .../tachiyomi/widget/SimpleNavigationView.kt | 130 -------------- .../listener/IgnoreFirstSpinnerListener.kt | 21 --- .../main/res/drawable/empty_drawable_32dp.xml | 7 - .../res/drawable/ic_arrow_down_white_32dp.xml | 15 -- .../res/drawable/ic_arrow_up_white_32dp.xml | 15 -- .../main/res/drawable/ic_check_box_24dp.xml | 9 - .../ic_check_box_outline_blank_24dp.xml | 9 - .../main/res/drawable/ic_check_box_x_24dp.xml | 9 - .../main/res/drawable/ic_expand_less_24dp.xml | 9 - .../res/layout/navigation_view_checkbox.xml | 24 --- .../layout/navigation_view_checkedtext.xml | 22 --- .../main/res/layout/navigation_view_group.xml | 29 --- .../main/res/layout/navigation_view_radio.xml | 22 --- .../res/layout/navigation_view_spinner.xml | 33 ---- .../main/res/layout/navigation_view_text.xml | 30 ---- .../main/res/layout/source_filter_sheet.xml | 47 ----- .../core/components/CollapsibleBox.kt | 56 ++++++ .../core/components/SettingsItems.kt | 153 ++++++++++++++++ 36 files changed, 459 insertions(+), 1305 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/CheckboxItem.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/GroupItem.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/HeaderItem.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SectionItems.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SelectItem.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SeparatorItem.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SortGroup.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SortItem.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TextItem.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TriStateItem.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/listener/IgnoreFirstSpinnerListener.kt delete mode 100644 app/src/main/res/drawable/empty_drawable_32dp.xml delete mode 100644 app/src/main/res/drawable/ic_arrow_down_white_32dp.xml delete mode 100644 app/src/main/res/drawable/ic_arrow_up_white_32dp.xml delete mode 100644 app/src/main/res/drawable/ic_check_box_24dp.xml delete mode 100644 app/src/main/res/drawable/ic_check_box_outline_blank_24dp.xml delete mode 100644 app/src/main/res/drawable/ic_check_box_x_24dp.xml delete mode 100644 app/src/main/res/drawable/ic_expand_less_24dp.xml delete mode 100644 app/src/main/res/layout/navigation_view_checkbox.xml delete mode 100644 app/src/main/res/layout/navigation_view_checkedtext.xml delete mode 100644 app/src/main/res/layout/navigation_view_group.xml delete mode 100644 app/src/main/res/layout/navigation_view_radio.xml delete mode 100644 app/src/main/res/layout/navigation_view_spinner.xml delete mode 100644 app/src/main/res/layout/navigation_view_text.xml delete mode 100644 app/src/main/res/layout/source_filter_sheet.xml create mode 100644 presentation-core/src/main/java/tachiyomi/presentation/core/components/CollapsibleBox.kt create mode 100644 presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt diff --git a/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt b/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt index 0db51ef4c5..57549e9904 100644 --- a/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt +++ b/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt @@ -1,52 +1,32 @@ package eu.kanade.presentation.components -import androidx.annotation.StringRes import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.material.ContentAlpha import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDownward -import androidx.compose.material.icons.filled.ArrowUpward import androidx.compose.material.icons.rounded.CheckBox import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank import androidx.compose.material.icons.rounded.DisabledByDefault -import androidx.compose.material3.Checkbox +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.RadioButton +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import tachiyomi.domain.manga.model.TriStateFilter -import tachiyomi.presentation.core.theme.header - -@Composable -fun HeadingItem( - @StringRes labelRes: Int, -) { - HeadingItem(stringResource(labelRes)) -} - -@Composable -fun HeadingItem( - text: String, -) { - Text( - text = text, - style = MaterialTheme.typography.header, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp), - ) -} +import tachiyomi.presentation.core.components.SettingsItemsPaddings @Composable fun TriStateItem( @@ -68,7 +48,7 @@ fun TriStateItem( }, ) .fillMaxWidth() - .padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp), + .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(24.dp), ) { @@ -99,87 +79,50 @@ fun TriStateItem( } @Composable -fun SortItem( +fun SelectItem( label: String, - sortDescending: Boolean?, - onClick: () -> Unit, + options: Array, + selectedIndex: Int, + onSelect: (Int) -> Unit, ) { - val arrowIcon = when (sortDescending) { - true -> Icons.Default.ArrowDownward - false -> Icons.Default.ArrowUpward - null -> null - } + var expanded by remember { mutableStateOf(false) } - Row( - modifier = Modifier - .clickable(onClick = onClick) - .fillMaxWidth() - .padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(24.dp), + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = !expanded }, ) { - if (arrowIcon != null) { - Icon( - imageVector = arrowIcon, - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - ) - } else { - Spacer(modifier = Modifier.size(24.dp)) + OutlinedTextField( + modifier = Modifier + .menuAnchor() + .fillMaxWidth() + .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), + label = { Text(text = label) }, + value = options[selectedIndex].toString(), + onValueChange = {}, + readOnly = true, + singleLine = true, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon( + expanded = expanded, + ) + }, + colors = ExposedDropdownMenuDefaults.textFieldColors(), + ) + + ExposedDropdownMenu( + modifier = Modifier.exposedDropdownSize(matchTextFieldWidth = true), + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + options.forEachIndexed { index, text -> + DropdownMenuItem( + text = { Text(text.toString()) }, + onClick = { + onSelect(index) + expanded = false + }, + ) + } } - Text( - text = label, - style = MaterialTheme.typography.bodyMedium, - ) - } -} - -@Composable -fun CheckboxItem( - label: String, - checked: Boolean, - onClick: () -> Unit, -) { - Row( - modifier = Modifier - .clickable(onClick = onClick) - .fillMaxWidth() - .padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(24.dp), - ) { - Checkbox( - checked = checked, - onCheckedChange = null, - ) - Text( - text = label, - style = MaterialTheme.typography.bodyMedium, - ) - } -} - -@Composable -fun RadioItem( - label: String, - selected: Boolean, - onClick: () -> Unit, -) { - Row( - modifier = Modifier - .clickable(onClick = onClick) - .fillMaxWidth() - .padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(24.dp), - ) { - RadioButton( - selected = selected, - onClick = null, - ) - Text( - text = label, - style = MaterialTheme.typography.bodyMedium, - ) } } diff --git a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt index 86acece98d..04913835e9 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt @@ -10,10 +10,6 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import eu.kanade.domain.library.service.LibraryPreferences -import eu.kanade.presentation.components.CheckboxItem -import eu.kanade.presentation.components.HeadingItem -import eu.kanade.presentation.components.RadioItem -import eu.kanade.presentation.components.SortItem import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialogPaddings import eu.kanade.presentation.components.TriStateItem @@ -27,6 +23,10 @@ import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.library.model.display import tachiyomi.domain.library.model.sort import tachiyomi.domain.manga.model.TriStateFilter +import tachiyomi.presentation.core.components.CheckboxItem +import tachiyomi.presentation.core.components.HeadingItem +import tachiyomi.presentation.core.components.RadioItem +import tachiyomi.presentation.core.components.SortItem @Composable fun LibrarySettingsDialog( diff --git a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt index 92ee2ca2e6..46a26045ba 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt @@ -25,14 +25,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.kanade.domain.manga.model.downloadedFilter import eu.kanade.domain.manga.model.forceDownloaded -import eu.kanade.presentation.components.RadioItem -import eu.kanade.presentation.components.SortItem import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialogPaddings import eu.kanade.presentation.components.TriStateItem import eu.kanade.tachiyomi.R import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.TriStateFilter +import tachiyomi.presentation.core.components.RadioItem +import tachiyomi.presentation.core.components.SortItem @Composable fun ChapterSettingsDialog( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt index 24d32aba5d..d4eb08653f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt @@ -8,13 +8,11 @@ import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource import androidx.paging.compose.collectAsLazyPagingItems @@ -45,7 +43,6 @@ data class SourceSearchScreen( @Composable override fun Content() { - val context = LocalContext.current val uriHandler = LocalUriHandler.current val navigator = LocalNavigator.currentOrThrow val scope = rememberCoroutineScope() @@ -123,9 +120,5 @@ data class SourceSearchScreen( } else -> {} } - - LaunchedEffect(state.filters) { - screenModel.initFilterSheet(context) - } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index 4705e89f2e..6e5342cbf7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -28,7 +28,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource @@ -93,7 +92,6 @@ data class BrowseSourceScreen( } val scope = rememberCoroutineScope() - val context = LocalContext.current val haptic = LocalHapticFeedback.current val uriHandler = LocalUriHandler.current val snackbarHostState = remember { SnackbarHostState() } @@ -231,7 +229,21 @@ data class BrowseSourceScreen( val onDismissRequest = { screenModel.setDialog(null) } when (val dialog = state.dialog) { - is BrowseSourceScreenModel.Dialog.Migrate -> {} + is BrowseSourceScreenModel.Dialog.Filter -> { + SourceFilterDialog( + onDismissRequest = onDismissRequest, + filters = state.filters, + onReset = { + screenModel.resetFilters() + }, + onFilter = { + screenModel.search(filters = state.filters) + }, + onUpdate = { + screenModel.setFilters(it) + }, + ) + } is BrowseSourceScreenModel.Dialog.AddDuplicateManga -> { DuplicateMangaDialog( onDismissRequest = onDismissRequest, @@ -259,13 +271,10 @@ data class BrowseSourceScreen( }, ) } + is BrowseSourceScreenModel.Dialog.Migrate -> {} else -> {} } - LaunchedEffect(state.filters) { - screenModel.initFilterSheet(context) - } - LaunchedEffect(Unit) { queryEvent.receiveAsFlow() .collectLatest { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt index e9e6e6f515..7f7df1b05d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.browse.source.browse -import android.content.Context import android.content.res.Configuration import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.runtime.Immutable @@ -14,7 +13,6 @@ import androidx.paging.filter import androidx.paging.map import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.coroutineScope -import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.core.prefs.asState import eu.kanade.domain.chapter.interactor.SetMangaDefaultChapterFlags import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay @@ -33,19 +31,6 @@ import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.ui.browse.source.filter.CheckboxItem -import eu.kanade.tachiyomi.ui.browse.source.filter.CheckboxSectionItem -import eu.kanade.tachiyomi.ui.browse.source.filter.GroupItem -import eu.kanade.tachiyomi.ui.browse.source.filter.HeaderItem -import eu.kanade.tachiyomi.ui.browse.source.filter.SelectItem -import eu.kanade.tachiyomi.ui.browse.source.filter.SelectSectionItem -import eu.kanade.tachiyomi.ui.browse.source.filter.SeparatorItem -import eu.kanade.tachiyomi.ui.browse.source.filter.SortGroup -import eu.kanade.tachiyomi.ui.browse.source.filter.SortItem -import eu.kanade.tachiyomi.ui.browse.source.filter.TextItem -import eu.kanade.tachiyomi.ui.browse.source.filter.TextSectionItem -import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateItem -import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem import eu.kanade.tachiyomi.util.removeCovers import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.distinctUntilChanged @@ -125,11 +110,6 @@ class BrowseSourceScreenModel( } } - /** - * Sheet containing filter items. - */ - private var filterSheet: SourceFilterSheet? = null - /** * Flow of Pager flow tied to [State.listing] */ @@ -175,6 +155,16 @@ class BrowseSourceScreenModel( mutableState.update { it.copy(listing = listing) } } + fun setFilters(filters: FilterList) { + if (source !is CatalogueSource) return + + mutableState.update { + it.copy( + filters = filters, + ) + } + } + fun search(query: String? = null, filters: FilterList? = null) { if (source !is CatalogueSource) return @@ -350,7 +340,7 @@ class BrowseSourceScreenModel( return getDuplicateLibraryManga.await(manga.title) } - fun moveMangaToCategories(manga: Manga, vararg categories: Category) { + private fun moveMangaToCategories(manga: Manga, vararg categories: Category) { moveMangaToCategories(manga, categories.filter { it.id != 0L }.map { it.id }) } @@ -364,7 +354,7 @@ class BrowseSourceScreenModel( } fun openFilterSheet() { - filterSheet?.show() + setDialog(Dialog.Filter) } fun setDialog(dialog: Dialog?) { @@ -375,23 +365,6 @@ class BrowseSourceScreenModel( mutableState.update { it.copy(toolbarQuery = query) } } - fun initFilterSheet(context: Context) { - if (state.value.filters.isEmpty()) { - return - } - - filterSheet = SourceFilterSheet( - context = context, - onFilterClicked = { search(filters = state.value.filters) }, - onResetClicked = { - resetFilters() - filterSheet?.setFilters(state.value.filterItems) - }, - ) - - filterSheet?.setFilters(state.value.filterItems) - } - sealed class Listing(open val query: String?, open val filters: FilterList) { object Popular : Listing(query = GetRemoteManga.QUERY_POPULAR, filters = FilterList()) object Latest : Listing(query = GetRemoteManga.QUERY_LATEST, filters = FilterList()) @@ -409,6 +382,7 @@ class BrowseSourceScreenModel( } sealed class Dialog { + object Filter : Dialog() data class RemoveManga(val manga: Manga) : Dialog() data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog() data class ChangeMangaCategory( @@ -425,43 +399,6 @@ class BrowseSourceScreenModel( val toolbarQuery: String? = null, val dialog: Dialog? = null, ) { - val filterItems get() = filters.toItems() val isUserQuery get() = listing is Listing.Search && !listing.query.isNullOrEmpty() } } - -private fun FilterList.toItems(): List> { - return mapNotNull { filter -> - when (filter) { - is SourceModelFilter.Header -> HeaderItem(filter) - is SourceModelFilter.Separator -> SeparatorItem(filter) - is SourceModelFilter.CheckBox -> CheckboxItem(filter) - is SourceModelFilter.TriState -> TriStateItem(filter) - is SourceModelFilter.Text -> TextItem(filter) - is SourceModelFilter.Select<*> -> SelectItem(filter) - is SourceModelFilter.Group<*> -> { - val group = GroupItem(filter) - val subItems = filter.state.mapNotNull { - when (it) { - is SourceModelFilter.CheckBox -> CheckboxSectionItem(it) - is SourceModelFilter.TriState -> TriStateSectionItem(it) - is SourceModelFilter.Text -> TextSectionItem(it) - is SourceModelFilter.Select<*> -> SelectSectionItem(it) - else -> null - } - } - subItems.forEach { it.header = group } - group.subItems = subItems - group - } - is SourceModelFilter.Sort -> { - val group = SortGroup(filter) - val subItems = filter.values.map { - SortItem(it, group) - } - group.subItems = subItems - group - } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt new file mode 100644 index 0000000000..be5b058b15 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt @@ -0,0 +1,165 @@ +package eu.kanade.tachiyomi.ui.browse.source.browse + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import eu.kanade.presentation.components.AdaptiveSheet +import eu.kanade.presentation.components.SelectItem +import eu.kanade.presentation.components.TriStateItem +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.widget.TriState +import eu.kanade.tachiyomi.widget.toTriStateFilter +import tachiyomi.presentation.core.components.CheckboxItem +import tachiyomi.presentation.core.components.CollapsibleBox +import tachiyomi.presentation.core.components.HeadingItem +import tachiyomi.presentation.core.components.LazyColumn +import tachiyomi.presentation.core.components.SortItem +import tachiyomi.presentation.core.components.TextItem +import tachiyomi.presentation.core.components.material.Button +import tachiyomi.presentation.core.components.material.Divider + +@Composable +fun SourceFilterDialog( + onDismissRequest: () -> Unit, + filters: FilterList, + onReset: () -> Unit, + onFilter: () -> Unit, + onUpdate: (FilterList) -> Unit, +) { + val updateFilters = { onUpdate(filters) } + + AdaptiveSheet( + onDismissRequest = onDismissRequest, + ) { contentPadding -> + LazyColumn( + contentPadding = contentPadding, + ) { + stickyHeader { + Row( + modifier = Modifier + .background(MaterialTheme.colorScheme.background) + .padding(8.dp), + ) { + TextButton(onClick = onReset) { + Text( + text = stringResource(R.string.action_reset), + style = LocalTextStyle.current.copy( + color = MaterialTheme.colorScheme.primary, + ), + ) + } + + Spacer(modifier = Modifier.weight(1f)) + + Button(onClick = onFilter) { + Text(stringResource(R.string.action_filter)) + } + } + Divider() + } + + items(filters) { + FilterItem(it, updateFilters) + } + } + } +} + +@Composable +private fun FilterItem(filter: Filter<*>, onUpdate: () -> Unit) { + when (filter) { + is Filter.Header -> { + HeadingItem(filter.name) + } + is Filter.Separator -> { + Divider() + } + is Filter.CheckBox -> { + CheckboxItem( + label = filter.name, + checked = filter.state, + ) { + filter.state = !filter.state + onUpdate() + } + } + is Filter.TriState -> { + TriStateItem( + label = filter.name, + state = filter.state.toTriStateFilter(), + ) { + filter.state = TriState.valueOf(filter.state).next().value + onUpdate() + } + } + is Filter.Text -> { + TextItem( + label = filter.name, + value = filter.state, + ) { + filter.state = it + onUpdate() + } + } + is Filter.Select<*> -> { + SelectItem( + label = filter.name, + options = filter.values, + selectedIndex = filter.state, + ) { + filter.state = it + onUpdate() + } + } + is Filter.Sort -> { + CollapsibleBox( + heading = filter.name, + ) { + Column { + filter.values.mapIndexed { index, item -> + SortItem( + label = item, + sortDescending = filter.state?.ascending?.not() + ?.takeIf { index == filter.state?.index }, + ) { + val ascending = if (index == filter.state?.index) { + !filter.state!!.ascending + } else { + filter.state!!.ascending + } + filter.state = Filter.Sort.Selection( + index = index, + ascending = ascending, + ) + onUpdate() + } + } + } + } + } + is Filter.Group<*> -> { + CollapsibleBox( + heading = filter.name, + ) { + Column { + filter.state + .filterIsInstance>() + .map { FilterItem(filter = it, onUpdate = onUpdate) } + } + } + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt deleted file mode 100644 index 109178d224..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterSheet.kt +++ /dev/null @@ -1,63 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.browse - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.databinding.SourceFilterSheetBinding -import eu.kanade.tachiyomi.widget.SimpleNavigationView -import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog - -class SourceFilterSheet( - context: Context, - private val onFilterClicked: () -> Unit, - private val onResetClicked: () -> Unit, -) : BaseBottomSheetDialog(context) { - - private var filterNavView: FilterNavigationView = FilterNavigationView(context) - - override fun createView(inflater: LayoutInflater): View { - filterNavView.onFilterClicked = { - onFilterClicked() - this.dismiss() - } - filterNavView.onResetClicked = onResetClicked - - return filterNavView - } - - fun setFilters(items: List>) { - filterNavView.adapter.updateDataSet(items) - } - - class FilterNavigationView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - ) : - SimpleNavigationView(context, attrs) { - - var onFilterClicked = {} - var onResetClicked = {} - - val adapter: FlexibleAdapter> = FlexibleAdapter>(null) - .setDisplayHeadersAtStartUp(true) - - private val binding = SourceFilterSheetBinding.inflate( - LayoutInflater.from(context), - null, - false, - ) - - init { - recycler.adapter = adapter - recycler.setHasFixedSize(true) - (binding.root.getChildAt(1) as ViewGroup).addView(recycler) - addView(binding.root) - binding.filterBtn.setOnClickListener { onFilterClicked() } - binding.resetBtn.setOnClickListener { onResetClicked() } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/CheckboxItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/CheckboxItem.kt deleted file mode 100644 index beb52ec52c..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/CheckboxItem.kt +++ /dev/null @@ -1,46 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import android.view.View -import android.widget.CheckBox -import androidx.recyclerview.widget.RecyclerView -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.model.Filter - -open class CheckboxItem(val filter: Filter.CheckBox) : AbstractFlexibleItem() { - - override fun getLayoutRes(): Int { - return R.layout.navigation_view_checkbox - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - val view = holder.check - view.text = filter.name - view.isChecked = filter.state - holder.itemView.setOnClickListener { - view.toggle() - filter.state = view.isChecked - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return filter == (other as CheckboxItem).filter - } - - override fun hashCode(): Int { - return filter.hashCode() - } - - class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { - val check: CheckBox = itemView.findViewById(R.id.nav_view_item) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/GroupItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/GroupItem.kt deleted file mode 100644 index 9bd64bc330..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/GroupItem.kt +++ /dev/null @@ -1,66 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import android.view.View -import android.widget.ImageView -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.flexibleadapter.items.ISectionable -import eu.davidea.viewholders.ExpandableViewHolder -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.util.view.setVectorCompat - -class GroupItem(val filter: Filter.Group<*>) : AbstractExpandableHeaderItem>() { - - init { - isExpanded = false - } - - override fun getLayoutRes(): Int { - return R.layout.navigation_view_group - } - - override fun getItemViewType(): Int { - return 101 - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - holder.title.text = filter.name - - holder.icon.setVectorCompat( - if (isExpanded) { - R.drawable.ic_expand_less_24dp - } else { - R.drawable.ic_expand_more_24dp - }, - ) - - holder.itemView.setOnClickListener(holder) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return filter == (other as GroupItem).filter - } - - override fun hashCode(): Int { - return filter.hashCode() - } - - open class Holder(view: View, adapter: FlexibleAdapter<*>) : ExpandableViewHolder(view, adapter, true) { - val title: TextView = itemView.findViewById(R.id.title) - val icon: ImageView = itemView.findViewById(R.id.expand_icon) - - override fun shouldNotifyParentOnClick(): Boolean { - return true - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/HeaderItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/HeaderItem.kt deleted file mode 100644 index 7c0692036d..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/HeaderItem.kt +++ /dev/null @@ -1,41 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import android.annotation.SuppressLint -import android.view.View -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.R -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.source.model.Filter - -class HeaderItem(val filter: Filter.Header) : AbstractHeaderItem() { - - @SuppressLint("PrivateResource") - override fun getLayoutRes(): Int { - return R.layout.design_navigation_item_subheader - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - val view = holder.itemView as TextView - view.text = filter.name - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return filter == (other as HeaderItem).filter - } - - override fun hashCode(): Int { - return filter.hashCode() - } - - class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SectionItems.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SectionItems.kt deleted file mode 100644 index 06d1b8a5d7..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SectionItems.kt +++ /dev/null @@ -1,100 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import eu.davidea.flexibleadapter.items.ISectionable -import eu.kanade.tachiyomi.source.model.Filter - -class TriStateSectionItem(filter: Filter.TriState) : TriStateItem(filter), ISectionable { - - private var head: GroupItem? = null - - override fun getHeader(): GroupItem? = head - - override fun setHeader(header: GroupItem?) { - head = header - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as TriStateSectionItem - if (head != other.head) return false - return filter == other.filter - } - - override fun hashCode(): Int { - return filter.hashCode() + (head?.hashCode() ?: 0) - } -} - -class TextSectionItem(filter: Filter.Text) : TextItem(filter), ISectionable { - - private var head: GroupItem? = null - - override fun getHeader(): GroupItem? = head - - override fun setHeader(header: GroupItem?) { - head = header - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as TextSectionItem - if (head != other.head) return false - return filter == other.filter - } - - override fun hashCode(): Int { - return filter.hashCode() + (head?.hashCode() ?: 0) - } -} - -class CheckboxSectionItem(filter: Filter.CheckBox) : CheckboxItem(filter), ISectionable { - - private var head: GroupItem? = null - - override fun getHeader(): GroupItem? = head - - override fun setHeader(header: GroupItem?) { - head = header - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as CheckboxSectionItem - if (head != other.head) return false - return filter == other.filter - } - - override fun hashCode(): Int { - return filter.hashCode() + (head?.hashCode() ?: 0) - } -} - -class SelectSectionItem(filter: Filter.Select<*>) : SelectItem(filter), ISectionable { - - private var head: GroupItem? = null - - override fun getHeader(): GroupItem? = head - - override fun setHeader(header: GroupItem?) { - head = header - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as SelectSectionItem - if (head != other.head) return false - return filter == other.filter - } - - override fun hashCode(): Int { - return filter.hashCode() + (head?.hashCode() ?: 0) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SelectItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SelectItem.kt deleted file mode 100644 index d64e6c89f5..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SelectItem.kt +++ /dev/null @@ -1,59 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import android.view.View -import android.widget.ArrayAdapter -import android.widget.Spinner -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.widget.listener.IgnoreFirstSpinnerListener - -open class SelectItem(val filter: Filter.Select<*>) : AbstractFlexibleItem() { - - override fun getLayoutRes(): Int { - return R.layout.navigation_view_spinner - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - holder.text.text = filter.name + ": " - - val spinner = holder.spinner - spinner.prompt = filter.name - spinner.adapter = ArrayAdapter( - holder.itemView.context, - android.R.layout.simple_spinner_item, - filter.values, - ).apply { - setDropDownViewResource(R.layout.common_spinner_item) - } - spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { pos -> - filter.state = pos - } - spinner.setSelection(filter.state) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return filter == (other as SelectItem).filter - } - - override fun hashCode(): Int { - return filter.hashCode() - } - - class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { - - val text: TextView = itemView.findViewById(R.id.nav_view_item_text) - val spinner: Spinner = itemView.findViewById(R.id.nav_view_item) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SeparatorItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SeparatorItem.kt deleted file mode 100644 index af21aeabb1..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SeparatorItem.kt +++ /dev/null @@ -1,38 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import android.annotation.SuppressLint -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.R -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.source.model.Filter - -class SeparatorItem(val filter: Filter.Separator) : AbstractHeaderItem() { - - @SuppressLint("PrivateResource") - override fun getLayoutRes(): Int { - return R.layout.design_navigation_item_separator - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return filter == (other as SeparatorItem).filter - } - - override fun hashCode(): Int { - return filter.hashCode() - } - - class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SortGroup.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SortGroup.kt deleted file mode 100644 index f648370045..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SortGroup.kt +++ /dev/null @@ -1,56 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.flexibleadapter.items.ISectionable -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.util.view.setVectorCompat - -class SortGroup(val filter: Filter.Sort) : AbstractExpandableHeaderItem>() { - - init { - isExpanded = false - } - - override fun getLayoutRes(): Int { - return R.layout.navigation_view_group - } - - override fun getItemViewType(): Int { - return 100 - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - holder.title.text = filter.name - - holder.icon.setVectorCompat( - if (isExpanded) { - R.drawable.ic_expand_less_24dp - } else { - R.drawable.ic_expand_more_24dp - }, - ) - - holder.itemView.setOnClickListener(holder) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return filter == (other as SortGroup).filter - } - - override fun hashCode(): Int { - return filter.hashCode() - } - - class Holder(view: View, adapter: FlexibleAdapter<*>) : GroupItem.Holder(view, adapter) -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SortItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SortItem.kt deleted file mode 100644 index 160856f827..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/SortItem.kt +++ /dev/null @@ -1,75 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import android.view.View -import android.widget.CheckedTextView -import androidx.appcompat.content.res.AppCompatResources -import androidx.recyclerview.widget.RecyclerView -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractSectionableItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.util.system.getResourceColor - -class SortItem(val name: String, val group: SortGroup) : AbstractSectionableItem(group) { - - override fun getLayoutRes(): Int { - return R.layout.navigation_view_checkedtext - } - - override fun getItemViewType(): Int { - return 102 - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - val view = holder.text - view.text = name - val filter = group.filter - - val i = filter.values.indexOf(name) - - fun getIcon() = when (filter.state) { - Filter.Sort.Selection(i, false) -> - AppCompatResources.getDrawable(view.context, R.drawable.ic_arrow_down_white_32dp) - ?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) } - Filter.Sort.Selection(i, true) -> - AppCompatResources.getDrawable(view.context, R.drawable.ic_arrow_up_white_32dp) - ?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) } - else -> AppCompatResources.getDrawable(view.context, R.drawable.empty_drawable_32dp) - } - - view.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null) - holder.itemView.setOnClickListener { - val pre = filter.state?.index ?: i - if (pre != i) { - filter.state = Filter.Sort.Selection(i, false) - } else { - filter.state = Filter.Sort.Selection(i, filter.state?.ascending == false) - } - - group.subItems.forEach { adapter.notifyItemChanged(adapter.getGlobalPositionOf(it)) } - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - other as SortItem - return name == other.name && group == other.group - } - - override fun hashCode(): Int { - var result = name.hashCode() - result = 31 * result + group.hashCode() - return result - } - - class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { - val text: CheckedTextView = itemView.findViewById(R.id.nav_view_item) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TextItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TextItem.kt deleted file mode 100644 index b0f5010aff..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TextItem.kt +++ /dev/null @@ -1,47 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import android.view.View -import android.widget.EditText -import androidx.core.widget.doOnTextChanged -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.textfield.TextInputLayout -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.model.Filter - -open class TextItem(val filter: Filter.Text) : AbstractFlexibleItem() { - - override fun getLayoutRes(): Int { - return R.layout.navigation_view_text - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - holder.wrapper.hint = filter.name - holder.edit.setText(filter.state) - holder.edit.doOnTextChanged { text, _, _, _ -> - filter.state = text.toString() - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return filter == (other as TextItem).filter - } - - override fun hashCode(): Int { - return filter.hashCode() - } - - class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { - val wrapper: TextInputLayout = itemView.findViewById(R.id.nav_view_item_wrapper) - val edit: EditText = itemView.findViewById(R.id.nav_view_item) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TriStateItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TriStateItem.kt deleted file mode 100644 index 04e4e1edc9..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/filter/TriStateItem.kt +++ /dev/null @@ -1,80 +0,0 @@ -package eu.kanade.tachiyomi.ui.browse.source.filter - -import android.view.View -import android.widget.CheckedTextView -import androidx.appcompat.content.res.AppCompatResources -import androidx.core.view.updatePadding -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.R -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.util.system.dpToPx -import eu.kanade.tachiyomi.util.system.getResourceColor -import eu.kanade.tachiyomi.R as TR - -open class TriStateItem(val filter: Filter.TriState) : AbstractFlexibleItem() { - - override fun getLayoutRes(): Int { - return TR.layout.navigation_view_checkedtext - } - - override fun getItemViewType(): Int { - return 103 - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - val view = holder.text - view.text = filter.name - - fun getIcon() = AppCompatResources.getDrawable( - view.context, - when (filter.state) { - Filter.TriState.STATE_IGNORE -> TR.drawable.ic_check_box_outline_blank_24dp - Filter.TriState.STATE_INCLUDE -> TR.drawable.ic_check_box_24dp - Filter.TriState.STATE_EXCLUDE -> TR.drawable.ic_check_box_x_24dp - else -> throw Exception("Unknown state") - }, - )?.apply { - val color = if (filter.state == Filter.TriState.STATE_IGNORE) { - view.context.getResourceColor(R.attr.colorOnBackground, 0.38f) - } else { - view.context.getResourceColor(R.attr.colorPrimary) - } - - setTint(color) - } - - view.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null) - holder.itemView.setOnClickListener { - filter.state = (filter.state + 1) % 3 - view.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null) - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return filter == (other as TriStateItem).filter - } - - override fun hashCode(): Int { - return filter.hashCode() - } - - class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { - val text: CheckedTextView = itemView.findViewById(TR.id.nav_view_item) - - init { - // Align with native checkbox - text.updatePadding(left = 4.dpToPx) - text.compoundDrawablePadding = 20.dpToPx - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt deleted file mode 100644 index 3c6bd01e76..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt +++ /dev/null @@ -1,130 +0,0 @@ -package eu.kanade.tachiyomi.widget - -import android.annotation.SuppressLint -import android.content.Context -import android.util.AttributeSet -import android.view.View -import android.view.ViewGroup -import android.widget.CheckBox -import android.widget.CheckedTextView -import android.widget.EditText -import android.widget.FrameLayout -import android.widget.RadioButton -import android.widget.Spinner -import android.widget.TextView -import androidx.appcompat.widget.TintTypedArray -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.R -import com.google.android.material.textfield.TextInputLayout -import eu.kanade.tachiyomi.util.view.inflate -import eu.kanade.tachiyomi.R as TR - -@Suppress("LeakingThis") -@SuppressLint("PrivateResource", "RestrictedApi") -open class SimpleNavigationView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, -) : FrameLayout(context, attrs, defStyleAttr) { - - /** - * Recycler view containing all the items. - */ - protected val recycler = RecyclerView(context) - - init { - // Custom attributes - val a = TintTypedArray.obtainStyledAttributes( - context, - attrs, - R.styleable.NavigationView, - defStyleAttr, - R.style.Widget_Design_NavigationView, - ) - - a.recycle() - - recycler.layoutManager = LinearLayoutManager(context) - } - - /** - * Base view holder. - */ - abstract class Holder(view: View) : RecyclerView.ViewHolder(view) - - /** - * Separator view holder. - */ - class SeparatorHolder(parent: ViewGroup) : - Holder(parent.inflate(R.layout.design_navigation_item_separator)) - - /** - * Header view holder. - */ - class HeaderHolder(parent: ViewGroup) : - Holder(parent.inflate(TR.layout.navigation_view_group)) { - - val title: TextView = itemView.findViewById(TR.id.title) - } - - /** - * Clickable view holder. - */ - abstract class ClickableHolder(view: View, listener: OnClickListener?) : Holder(view) { - init { - itemView.setOnClickListener(listener) - } - } - - /** - * Radio view holder. - */ - class RadioHolder(parent: ViewGroup, listener: OnClickListener?) : - ClickableHolder(parent.inflate(TR.layout.navigation_view_radio), listener) { - - val radio: RadioButton = itemView.findViewById(TR.id.nav_view_item) - } - - /** - * Checkbox view holder. - */ - class CheckboxHolder(parent: ViewGroup, listener: OnClickListener?) : - ClickableHolder(parent.inflate(TR.layout.navigation_view_checkbox), listener) { - - val check: CheckBox = itemView.findViewById(TR.id.nav_view_item) - } - - /** - * Multi state view holder. - */ - class MultiStateHolder(parent: ViewGroup, listener: OnClickListener?) : - ClickableHolder(parent.inflate(TR.layout.navigation_view_checkedtext), listener) { - - val text: CheckedTextView = itemView.findViewById(TR.id.nav_view_item) - } - - class SpinnerHolder(parent: ViewGroup, listener: OnClickListener? = null) : - ClickableHolder(parent.inflate(TR.layout.navigation_view_spinner), listener) { - - val text: TextView = itemView.findViewById(TR.id.nav_view_item_text) - val spinner: Spinner = itemView.findViewById(TR.id.nav_view_item) - } - - class EditTextHolder(parent: ViewGroup) : - Holder(parent.inflate(TR.layout.navigation_view_text)) { - - val wrapper: TextInputLayout = itemView.findViewById(TR.id.nav_view_item_wrapper) - val edit: EditText = itemView.findViewById(TR.id.nav_view_item) - } - - protected companion object { - const val VIEW_TYPE_HEADER = 100 - const val VIEW_TYPE_SEPARATOR = 101 - const val VIEW_TYPE_RADIO = 102 - const val VIEW_TYPE_CHECKBOX = 103 - const val VIEW_TYPE_MULTISTATE = 104 - const val VIEW_TYPE_TEXT = 105 - const val VIEW_TYPE_LIST = 106 - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/listener/IgnoreFirstSpinnerListener.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/listener/IgnoreFirstSpinnerListener.kt deleted file mode 100644 index 38c946e017..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/listener/IgnoreFirstSpinnerListener.kt +++ /dev/null @@ -1,21 +0,0 @@ -package eu.kanade.tachiyomi.widget.listener - -import android.view.View -import android.widget.AdapterView -import android.widget.AdapterView.OnItemSelectedListener - -class IgnoreFirstSpinnerListener(private val block: (Int) -> Unit) : OnItemSelectedListener { - - private var firstEvent = true - - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - if (!firstEvent) { - block(position) - } else { - firstEvent = false - } - } - - override fun onNothingSelected(parent: AdapterView<*>?) { - } -} diff --git a/app/src/main/res/drawable/empty_drawable_32dp.xml b/app/src/main/res/drawable/empty_drawable_32dp.xml deleted file mode 100644 index 5658901914..0000000000 --- a/app/src/main/res/drawable/empty_drawable_32dp.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_arrow_down_white_32dp.xml b/app/src/main/res/drawable/ic_arrow_down_white_32dp.xml deleted file mode 100644 index fadc173d49..0000000000 --- a/app/src/main/res/drawable/ic_arrow_down_white_32dp.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_arrow_up_white_32dp.xml b/app/src/main/res/drawable/ic_arrow_up_white_32dp.xml deleted file mode 100644 index 9ce9ae5f59..0000000000 --- a/app/src/main/res/drawable/ic_arrow_up_white_32dp.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_check_box_24dp.xml b/app/src/main/res/drawable/ic_check_box_24dp.xml deleted file mode 100644 index f3f55d2cad..0000000000 --- a/app/src/main/res/drawable/ic_check_box_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_check_box_outline_blank_24dp.xml b/app/src/main/res/drawable/ic_check_box_outline_blank_24dp.xml deleted file mode 100644 index 1e39096b7e..0000000000 --- a/app/src/main/res/drawable/ic_check_box_outline_blank_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_check_box_x_24dp.xml b/app/src/main/res/drawable/ic_check_box_x_24dp.xml deleted file mode 100644 index bd6a98ffa8..0000000000 --- a/app/src/main/res/drawable/ic_check_box_x_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_expand_less_24dp.xml b/app/src/main/res/drawable/ic_expand_less_24dp.xml deleted file mode 100644 index 7f427269da..0000000000 --- a/app/src/main/res/drawable/ic_expand_less_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/navigation_view_checkbox.xml b/app/src/main/res/layout/navigation_view_checkbox.xml deleted file mode 100644 index 882a7d85d7..0000000000 --- a/app/src/main/res/layout/navigation_view_checkbox.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/navigation_view_checkedtext.xml b/app/src/main/res/layout/navigation_view_checkedtext.xml deleted file mode 100644 index 70b8729f7a..0000000000 --- a/app/src/main/res/layout/navigation_view_checkedtext.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/navigation_view_group.xml b/app/src/main/res/layout/navigation_view_group.xml deleted file mode 100644 index 8c3c90303b..0000000000 --- a/app/src/main/res/layout/navigation_view_group.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/navigation_view_radio.xml b/app/src/main/res/layout/navigation_view_radio.xml deleted file mode 100644 index 4f8d00e9d1..0000000000 --- a/app/src/main/res/layout/navigation_view_radio.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/navigation_view_spinner.xml b/app/src/main/res/layout/navigation_view_spinner.xml deleted file mode 100644 index 3d8d35efd2..0000000000 --- a/app/src/main/res/layout/navigation_view_spinner.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/navigation_view_text.xml b/app/src/main/res/layout/navigation_view_text.xml deleted file mode 100644 index 3b18c56073..0000000000 --- a/app/src/main/res/layout/navigation_view_text.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/layout/source_filter_sheet.xml b/app/src/main/res/layout/source_filter_sheet.xml deleted file mode 100644 index c71473b2a1..0000000000 --- a/app/src/main/res/layout/source_filter_sheet.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - -