diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 58388547c7..b9a2744741 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -67,6 +67,8 @@ object PreferenceKeys { const val enabledLanguages = "source_languages" + const val sourcesSort = "sources_sort" + const val backupDirectory = "backup_directory" const val downloadsDirectory = "download_directory" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index dad085346b..17cbc29e0e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -98,6 +98,8 @@ class PreferencesHelper(val context: Context) { fun enabledLanguages() = rxPrefs.getStringSet(Keys.enabledLanguages, setOf("en", Locale.getDefault().language)) + fun sourceSorting() = rxPrefs.getInteger(Keys.sourcesSort, 0) + fun sourceUsername(source: Source) = prefs.getString(Keys.sourceUsername(source.id), "") fun sourcePassword(source: Source) = prefs.getString(Keys.sourcePassword(source.id), "") diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt index f5fb95bbfe..0097660e91 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt @@ -1,9 +1,13 @@ package eu.kanade.tachiyomi.ui.catalogue import android.Manifest.permission.WRITE_EXTERNAL_STORAGE -import androidx.recyclerview.widget.LinearLayoutManager +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.appcompat.widget.SearchView -import android.view.* import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.RouterTransaction @@ -194,7 +198,7 @@ class CatalogueController : NucleusController(), .subscribeUntilDestroy { performGlobalSearch(it.queryText().toString()) } } - fun performGlobalSearch(query: String){ + private fun performGlobalSearch(query: String){ router.pushController(CatalogueSearchController(query).withFadeTransaction()) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt index 71b7999307..cb9c5dbb28 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt @@ -81,7 +81,7 @@ open class ExtensionController : NucleusController(), override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.action_settings -> { + R.id.action_filter -> { router.pushController((RouterTransaction.with(SettingsExtensionsController())) .popChangeHandler(SettingsExtensionsFadeChangeHandler()) .pushChangeHandler(FadeChangeHandler())) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt index 98cfd1b494..150e19ee35 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt @@ -1,8 +1,14 @@ package eu.kanade.tachiyomi.ui.setting import android.graphics.drawable.Drawable +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import androidx.appcompat.widget.SearchView +import androidx.preference.CheckBoxPreference import androidx.preference.PreferenceGroup import androidx.preference.PreferenceScreen +import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.source.SourceManager @@ -14,37 +20,47 @@ import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog import eu.kanade.tachiyomi.widget.preference.SwitchPreferenceCategory import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.* +import java.util.TreeMap class SettingsSourcesController : SettingsController(), SourceLoginDialog.Listener { + init { + setHasOptionsMenu(true) + } + private val onlineSources by lazy { Injekt.get().getOnlineSources() } + private var query = "" + + private var orderedLangs = listOf() + private var langPrefs = mutableListOf>() + private var sourcesByLang:TreeMap> = TreeMap() + private var sorting = SourcesSort.Alpha + override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { titleRes = R.string.pref_category_sources - + sorting = SourcesSort.from(preferences.sourceSorting().getOrDefault()) ?: SourcesSort.Alpha + activity?.invalidateOptionsMenu() // Get the list of active language codes. val activeLangsCodes = preferences.enabledLanguages().getOrDefault() // Get a map of sources grouped by language. - val sourcesByLang = onlineSources.groupByTo(TreeMap(), { it.lang }) + sourcesByLang = onlineSources.groupByTo(TreeMap(), { it.lang }) // Order first by active languages, then inactive ones - val orderedLangs = sourcesByLang.keys.filter { it in activeLangsCodes } + - sourcesByLang.keys.filterNot { it in activeLangsCodes } + orderedLangs = sourcesByLang.keys.filter { it in activeLangsCodes } + sourcesByLang.keys + .filterNot { it in activeLangsCodes } orderedLangs.forEach { lang -> - val sources = sourcesByLang[lang].orEmpty().sortedBy { it.name } - // Create a preference group and set initial state and change listener - SwitchPreferenceCategory(context).apply { + langPrefs.add(Pair(lang, SwitchPreferenceCategory(context).apply { preferenceScreen.addPreference(this) title = LocaleHelper.getDisplayName(lang, context) isPersistent = false if (lang in activeLangsCodes) { setChecked(true) - addLanguageSources(this, sources) + addLanguageSources(this, sortedSources(sourcesByLang[lang])) } onChange { newValue -> @@ -55,11 +71,11 @@ class SettingsSourcesController : SettingsController(), removeAll() } else { preferences.enabledLanguages().set(current + lang) - addLanguageSources(this, sources) + addLanguageSources(this, sortedSources(sourcesByLang[lang])) } true } - } + })) } } @@ -75,6 +91,29 @@ class SettingsSourcesController : SettingsController(), private fun addLanguageSources(group: PreferenceGroup, sources: List) { val hiddenCatalogues = preferences.hiddenCatalogues().getOrDefault() + val selectAllPreference = CheckBoxPreference(group.context).apply { + + title = "\t\t${context.getString(R.string.pref_category_all_sources)}" + key = "all_${sources.first().lang}" + isPersistent = false + isChecked = sources.all { it.id.toString() !in hiddenCatalogues } + isVisible = query.isEmpty() + + onChange { newValue -> + val checked = newValue as Boolean + val current = preferences.hiddenCatalogues().getOrDefault() + if (checked) + current.removeAll(sources.map { it.id.toString() }) + else + current.addAll(sources.map { it.id.toString() }) + preferences.hiddenCatalogues().set(current) + group.removeAll() + addLanguageSources(group, sortedSources(sources)) + true + } + } + group.addPreference(selectAllPreference) + sources.forEach { source -> val sourcePreference = LoginCheckBoxPreference(group.context, source).apply { val id = source.id.toString() @@ -82,16 +121,19 @@ class SettingsSourcesController : SettingsController(), key = getSourceKey(source.id) isPersistent = false isChecked = id !in hiddenCatalogues + isVisible = query.isEmpty() || source.name.contains(query, ignoreCase = true) onChange { newValue -> val checked = newValue as Boolean val current = preferences.hiddenCatalogues().getOrDefault() - preferences.hiddenCatalogues().set(if (checked) - current - id - else - current + id) + preferences.hiddenCatalogues().set( + if (checked) current - id + else current + id + ) + group.removeAll() + addLanguageSources(group, sortedSources(sources)) true } @@ -115,4 +157,90 @@ class SettingsSourcesController : SettingsController(), return "source_$sourceId" } + /** + * Adds items to the options menu. + * + * @param menu menu containing options. + * @param inflater used to load the menu xml. + */ + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.settings_sources, menu) + if (sorting == SourcesSort.Alpha) menu.findItem(R.id.action_sort_alpha).isChecked = true + else menu.findItem(R.id.action_sort_enabled).isChecked = true + + val searchItem = menu.findItem(R.id.action_search) + val searchView = searchItem.actionView as SearchView + searchView.maxWidth = Int.MAX_VALUE + + if (query.isNotEmpty()) { + searchItem.expandActionView() + searchView.setQuery(query, true) + searchView.clearFocus() + } + + searchView.queryTextChanges().filter { router.backstack.lastOrNull()?.controller() == this } + .subscribeUntilDestroy { + query = it.toString() + drawSources() + } + + // Fixes problem with the overflow icon showing up in lieu of search + searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { + override fun onMenuItemActionExpand(item: MenuItem): Boolean { + return true + } + + override fun onMenuItemActionCollapse(item: MenuItem): Boolean { + activity?.invalidateOptionsMenu() + return true + } + }) + } + + private fun drawSources() { + val activeLangsCodes = preferences.enabledLanguages().getOrDefault() + langPrefs.forEach { group -> + if (group.first in activeLangsCodes) { + group.second.removeAll() + addLanguageSources(group.second, sortedSources(sourcesByLang[group.first])) + } + } + } + + private fun sortedSources(sources: List?): List { + val sourceAlpha = sources.orEmpty().sortedBy { it.name } + return if (sorting == SourcesSort.Enabled) { + val hiddenCatalogues = preferences.hiddenCatalogues().getOrDefault() + sourceAlpha.filter { it.id.toString() !in hiddenCatalogues } + + sourceAlpha.filterNot { it.id.toString() !in hiddenCatalogues } + } else { + sourceAlpha + } + } + + /** + * Called when an option menu item has been selected by the user. + * + * @param item The selected item. + * @return True if this event has been consumed, false if it has not. + */ + override fun onOptionsItemSelected(item: MenuItem): Boolean { + sorting = when (item.itemId) { + R.id.action_sort_alpha -> SourcesSort.Alpha + R.id.action_sort_enabled -> SourcesSort.Enabled + else -> return super.onOptionsItemSelected(item) + } + item.isChecked = true + preferences.sourceSorting().set(sorting.value) + drawSources() + return true + } + + enum class SourcesSort(val value: Int) { + Alpha(0), Enabled(1); + + companion object { + fun from(i: Int): SourcesSort? = values().find { it.value == i } + } + } } \ No newline at end of file diff --git a/app/src/main/res/menu/extension_main.xml b/app/src/main/res/menu/extension_main.xml index 7a6ffffbeb..f64ef97801 100644 --- a/app/src/main/res/menu/extension_main.xml +++ b/app/src/main/res/menu/extension_main.xml @@ -9,7 +9,7 @@ app:actionViewClass="androidx.appcompat.widget.SearchView"/> diff --git a/app/src/main/res/menu/settings_sources.xml b/app/src/main/res/menu/settings_sources.xml new file mode 100644 index 0000000000..58a2ac856f --- /dev/null +++ b/app/src/main/res/menu/settings_sources.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bff1857c2a..a6003a8ed5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,6 +38,7 @@ Read Remove filter Alphabetically + Enabled Total chapters Last read Last updated @@ -116,6 +117,7 @@ Reader Downloads Sources + All Sources Tracking Advanced About