11package eu.kanade.tachiyomi.ui.setting
22
33import android.graphics.drawable.Drawable
4+ import android.view.Menu
5+ import android.view.MenuInflater
6+ import android.view.MenuItem
7+ import androidx.appcompat.widget.SearchView
8+ import androidx.preference.CheckBoxPreference
49import androidx.preference.PreferenceGroup
510import androidx.preference.PreferenceScreen
11+ import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges
612import eu.kanade.tachiyomi.R
713import eu.kanade.tachiyomi.data.preference.getOrDefault
814import eu.kanade.tachiyomi.source.SourceManager
@@ -14,37 +20,47 @@ import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog
1420import eu.kanade.tachiyomi.widget.preference.SwitchPreferenceCategory
1521import uy.kohesive.injekt.Injekt
1622import uy.kohesive.injekt.api.get
17- import java.util.*
23+ import java.util.TreeMap
1824
1925class SettingsSourcesController : SettingsController (),
2026 SourceLoginDialog .Listener {
2127
28+ init {
29+ setHasOptionsMenu(true )
30+ }
31+
2232 private val onlineSources by lazy { Injekt .get<SourceManager >().getOnlineSources() }
2333
34+ private var query = " "
35+
36+ private var orderedLangs = listOf<String >()
37+ private var langPrefs = mutableListOf<Pair <String , SwitchPreferenceCategory >>()
38+ private var sourcesByLang: TreeMap <String , MutableList <HttpSource >> = TreeMap ()
39+ private var sorting = SourcesSort .Alpha
40+
2441 override fun setupPreferenceScreen (screen : PreferenceScreen ) = with (screen) {
2542 titleRes = R .string.pref_category_sources
26-
43+ sorting = SourcesSort .from(preferences.sourceSorting().getOrDefault()) ? : SourcesSort .Alpha
44+ activity?.invalidateOptionsMenu()
2745 // Get the list of active language codes.
2846 val activeLangsCodes = preferences.enabledLanguages().getOrDefault()
2947
3048 // Get a map of sources grouped by language.
31- val sourcesByLang = onlineSources.groupByTo(TreeMap (), { it.lang })
49+ sourcesByLang = onlineSources.groupByTo(TreeMap (), { it.lang })
3250
3351 // Order first by active languages, then inactive ones
34- val orderedLangs = sourcesByLang.keys.filter { it in activeLangsCodes } +
35- sourcesByLang.keys .filterNot { it in activeLangsCodes }
52+ orderedLangs = sourcesByLang.keys.filter { it in activeLangsCodes } + sourcesByLang.keys
53+ .filterNot { it in activeLangsCodes }
3654
3755 orderedLangs.forEach { lang ->
38- val sources = sourcesByLang[lang].orEmpty().sortedBy { it.name }
39-
4056 // Create a preference group and set initial state and change listener
41- SwitchPreferenceCategory (context).apply {
57+ langPrefs.add( Pair (lang, SwitchPreferenceCategory (context).apply {
4258 preferenceScreen.addPreference(this )
4359 title = LocaleHelper .getDisplayName(lang, context)
4460 isPersistent = false
4561 if (lang in activeLangsCodes) {
4662 setChecked(true )
47- addLanguageSources(this , sources )
63+ addLanguageSources(this , sortedSources(sourcesByLang[lang]) )
4864 }
4965
5066 onChange { newValue ->
@@ -55,11 +71,11 @@ class SettingsSourcesController : SettingsController(),
5571 removeAll()
5672 } else {
5773 preferences.enabledLanguages().set(current + lang)
58- addLanguageSources(this , sources )
74+ addLanguageSources(this , sortedSources(sourcesByLang[lang]) )
5975 }
6076 true
6177 }
62- }
78+ }))
6379 }
6480 }
6581
@@ -75,23 +91,49 @@ class SettingsSourcesController : SettingsController(),
7591 private fun addLanguageSources (group : PreferenceGroup , sources : List <HttpSource >) {
7692 val hiddenCatalogues = preferences.hiddenCatalogues().getOrDefault()
7793
94+ val selectAllPreference = CheckBoxPreference (group.context).apply {
95+
96+ title = " \t\t ${context.getString(R .string.pref_category_all_sources)} "
97+ key = " all_${sources.first().lang} "
98+ isPersistent = false
99+ isChecked = sources.all { it.id.toString() !in hiddenCatalogues }
100+ isVisible = query.isEmpty()
101+
102+ onChange { newValue ->
103+ val checked = newValue as Boolean
104+ val current = preferences.hiddenCatalogues().getOrDefault()
105+ if (checked)
106+ current.removeAll(sources.map { it.id.toString() })
107+ else
108+ current.addAll(sources.map { it.id.toString() })
109+ preferences.hiddenCatalogues().set(current)
110+ group.removeAll()
111+ addLanguageSources(group, sortedSources(sources))
112+ true
113+ }
114+ }
115+ group.addPreference(selectAllPreference)
116+
78117 sources.forEach { source ->
79118 val sourcePreference = LoginCheckBoxPreference (group.context, source).apply {
80119 val id = source.id.toString()
81120 title = source.name
82121 key = getSourceKey(source.id)
83122 isPersistent = false
84123 isChecked = id !in hiddenCatalogues
124+ isVisible = query.isEmpty() || source.name.contains(query, ignoreCase = true )
85125
86126 onChange { newValue ->
87127 val checked = newValue as Boolean
88128 val current = preferences.hiddenCatalogues().getOrDefault()
89129
90- preferences.hiddenCatalogues().set(if (checked)
91- current - id
92- else
93- current + id )
130+ preferences.hiddenCatalogues().set(
131+ if (checked) current - id
132+ else current + id
133+ )
94134
135+ group.removeAll()
136+ addLanguageSources(group, sortedSources(sources))
95137 true
96138 }
97139
@@ -115,4 +157,90 @@ class SettingsSourcesController : SettingsController(),
115157 return " source_$sourceId "
116158 }
117159
160+ /* *
161+ * Adds items to the options menu.
162+ *
163+ * @param menu menu containing options.
164+ * @param inflater used to load the menu xml.
165+ */
166+ override fun onCreateOptionsMenu (menu : Menu , inflater : MenuInflater ) {
167+ inflater.inflate(R .menu.settings_sources, menu)
168+ if (sorting == SourcesSort .Alpha ) menu.findItem(R .id.action_sort_alpha).isChecked = true
169+ else menu.findItem(R .id.action_sort_enabled).isChecked = true
170+
171+ val searchItem = menu.findItem(R .id.action_search)
172+ val searchView = searchItem.actionView as SearchView
173+ searchView.maxWidth = Int .MAX_VALUE
174+
175+ if (query.isNotEmpty()) {
176+ searchItem.expandActionView()
177+ searchView.setQuery(query, true )
178+ searchView.clearFocus()
179+ }
180+
181+ searchView.queryTextChanges().filter { router.backstack.lastOrNull()?.controller() == this }
182+ .subscribeUntilDestroy {
183+ query = it.toString()
184+ drawSources()
185+ }
186+
187+ // Fixes problem with the overflow icon showing up in lieu of search
188+ searchItem.setOnActionExpandListener(object : MenuItem .OnActionExpandListener {
189+ override fun onMenuItemActionExpand (item : MenuItem ): Boolean {
190+ return true
191+ }
192+
193+ override fun onMenuItemActionCollapse (item : MenuItem ): Boolean {
194+ activity?.invalidateOptionsMenu()
195+ return true
196+ }
197+ })
198+ }
199+
200+ private fun drawSources () {
201+ val activeLangsCodes = preferences.enabledLanguages().getOrDefault()
202+ langPrefs.forEach { group ->
203+ if (group.first in activeLangsCodes) {
204+ group.second.removeAll()
205+ addLanguageSources(group.second, sortedSources(sourcesByLang[group.first]))
206+ }
207+ }
208+ }
209+
210+ private fun sortedSources (sources : List <HttpSource >? ): List <HttpSource > {
211+ val sourceAlpha = sources.orEmpty().sortedBy { it.name }
212+ return if (sorting == SourcesSort .Enabled ) {
213+ val hiddenCatalogues = preferences.hiddenCatalogues().getOrDefault()
214+ sourceAlpha.filter { it.id.toString() !in hiddenCatalogues } +
215+ sourceAlpha.filterNot { it.id.toString() !in hiddenCatalogues }
216+ } else {
217+ sourceAlpha
218+ }
219+ }
220+
221+ /* *
222+ * Called when an option menu item has been selected by the user.
223+ *
224+ * @param item The selected item.
225+ * @return True if this event has been consumed, false if it has not.
226+ */
227+ override fun onOptionsItemSelected (item : MenuItem ): Boolean {
228+ sorting = when (item.itemId) {
229+ R .id.action_sort_alpha -> SourcesSort .Alpha
230+ R .id.action_sort_enabled -> SourcesSort .Enabled
231+ else -> return super .onOptionsItemSelected(item)
232+ }
233+ item.isChecked = true
234+ preferences.sourceSorting().set(sorting.value)
235+ drawSources()
236+ return true
237+ }
238+
239+ enum class SourcesSort (val value : Int ) {
240+ Alpha (0 ), Enabled (1 );
241+
242+ companion object {
243+ fun from (i : Int ): SourcesSort ? = values().find { it.value == i }
244+ }
245+ }
118246}
0 commit comments