Skip to content

Commit 5448a16

Browse files
committed
Added search/sorting/mass enable/disable to catalouge sources
1 parent dd214f9 commit 5448a16

8 files changed

Lines changed: 186 additions & 20 deletions

File tree

app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ object PreferenceKeys {
6767

6868
const val enabledLanguages = "source_languages"
6969

70+
const val sourcesSort = "sources_sort"
71+
7072
const val backupDirectory = "backup_directory"
7173

7274
const val downloadsDirectory = "download_directory"

app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ class PreferencesHelper(val context: Context) {
9898

9999
fun enabledLanguages() = rxPrefs.getStringSet(Keys.enabledLanguages, setOf("en", Locale.getDefault().language))
100100

101+
fun sourceSorting() = rxPrefs.getInteger(Keys.sourcesSort, 0)
102+
101103
fun sourceUsername(source: Source) = prefs.getString(Keys.sourceUsername(source.id), "")
102104

103105
fun sourcePassword(source: Source) = prefs.getString(Keys.sourcePassword(source.id), "")

app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package eu.kanade.tachiyomi.ui.catalogue
22

33
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
4-
import androidx.recyclerview.widget.LinearLayoutManager
4+
import android.view.LayoutInflater
5+
import android.view.Menu
6+
import android.view.MenuInflater
7+
import android.view.MenuItem
8+
import android.view.View
9+
import android.view.ViewGroup
510
import androidx.appcompat.widget.SearchView
6-
import android.view.*
711
import com.bluelinelabs.conductor.ControllerChangeHandler
812
import com.bluelinelabs.conductor.ControllerChangeType
913
import com.bluelinelabs.conductor.RouterTransaction
@@ -194,7 +198,7 @@ class CatalogueController : NucleusController<CataloguePresenter>(),
194198
.subscribeUntilDestroy { performGlobalSearch(it.queryText().toString()) }
195199
}
196200

197-
fun performGlobalSearch(query: String){
201+
private fun performGlobalSearch(query: String){
198202
router.pushController(CatalogueSearchController(query).withFadeTransaction())
199203
}
200204

app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ open class ExtensionController : NucleusController<ExtensionPresenter>(),
8181

8282
override fun onOptionsItemSelected(item: MenuItem): Boolean {
8383
when (item.itemId) {
84-
R.id.action_settings -> {
84+
R.id.action_filter -> {
8585
router.pushController((RouterTransaction.with(SettingsExtensionsController()))
8686
.popChangeHandler(SettingsExtensionsFadeChangeHandler())
8787
.pushChangeHandler(FadeChangeHandler()))

app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt

Lines changed: 143 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package eu.kanade.tachiyomi.ui.setting
22

33
import 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
49
import androidx.preference.PreferenceGroup
510
import androidx.preference.PreferenceScreen
11+
import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges
612
import eu.kanade.tachiyomi.R
713
import eu.kanade.tachiyomi.data.preference.getOrDefault
814
import eu.kanade.tachiyomi.source.SourceManager
@@ -14,37 +20,47 @@ import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog
1420
import eu.kanade.tachiyomi.widget.preference.SwitchPreferenceCategory
1521
import uy.kohesive.injekt.Injekt
1622
import uy.kohesive.injekt.api.get
17-
import java.util.*
23+
import java.util.TreeMap
1824

1925
class 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
}

app/src/main/res/menu/extension_main.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
1010

1111
<item
12-
android:id="@+id/action_settings"
12+
android:id="@+id/action_filter"
1313
android:title="@string/action_filter"
1414
android:icon="@drawable/ic_filter_list_white_24dp"
1515
app:showAsAction="always"/>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<menu xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto">
4+
<item
5+
android:id="@+id/action_search"
6+
android:title="@string/action_search"
7+
android:icon="@drawable/ic_search_white_24dp"
8+
app:showAsAction="collapseActionView|ifRoom"
9+
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
10+
11+
<item
12+
android:id="@+id/action_sort"
13+
android:title="@string/action_sort"
14+
android:icon="@drawable/ic_sort_white_24dp"
15+
app:showAsAction="ifRoom">
16+
<menu>
17+
<group android:id="@+id/group"
18+
android:checkableBehavior="single">
19+
<item
20+
android:id="@+id/action_sort_alpha"
21+
android:title="@string/action_sort_alpha"/>
22+
<item
23+
android:id="@+id/action_sort_enabled"
24+
android:title="@string/action_sort_enabled"/>
25+
</group>
26+
</menu>
27+
</item>
28+
</menu>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
<string name="action_filter_read">Read</string>
3939
<string name="action_filter_empty">Remove filter</string>
4040
<string name="action_sort_alpha">Alphabetically</string>
41+
<string name="action_sort_enabled">Enabled</string>
4142
<string name="action_sort_total">Total chapters</string>
4243
<string name="action_sort_last_read">Last read</string>
4344
<string name="action_sort_last_updated">Last updated</string>
@@ -116,6 +117,7 @@
116117
<string name="pref_category_reader">Reader</string>
117118
<string name="pref_category_downloads">Downloads</string>
118119
<string name="pref_category_sources">Sources</string>
120+
<string name="pref_category_all_sources">All Sources</string>
119121
<string name="pref_category_tracking">Tracking</string>
120122
<string name="pref_category_advanced">Advanced</string>
121123
<string name="pref_category_about">About</string>

0 commit comments

Comments
 (0)