#3520 Searchable Settings (#3683)

* Adding class stubs for settings search, UI elements.

* -  implement searchable settings
- `SettingsController.setupPreferenceScreen` must return a PreferenceScreen

* Remove unneeded SettingsControllerFactory.

* Set query hint, clean up code smell.

* Add search button to MoreController, stop infinite recursion.

* - initialize SearchResultCollection once in Activity.onCreate

* - implement prefernce highlighting after settings search

* - Ensure all Preferences have a key set or else the highlighting effect will have no effect on it.
- remove ExtensionFilterController and SourceFilterController from settingControllersList in SettingsSearchHelper, since those are related to Extensions and not Settings

* Limiting search to settings menu only, localized breadcrumb string, and code cleanup after code review.

* - moved call to SettingsSearchHelper.initPreferenceSearchResultCollection() into SettingsSearchController

* Code review cleanup and refactoring.

* Inlined non-reused key strings.

* Adding more UI polish, add comments for future enhancements.

* - retain search query when navigating *away* from SettingsSearchController
- keep `searchItem` in `expandActionView` state until user goes back (fixes the empty view in `SettingsSearchSearchController` issue)

Co-authored-by: mpm11011 <markuscicero5@gmail.com>
Co-authored-by: lmj0011 <9396189+lmj0011@users.noreply.github.com>
(cherry picked from commit 766f9e37b52873080c58d73dfaff42c5add24dc0)

# Conflicts:
#	app/build.gradle
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourceFilterController.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/more/AboutController.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt
This commit is contained in:
arkon 2020-09-22 22:23:38 -04:00 committed by Jobobby04
parent 89eea4ab91
commit 26557b3257
30 changed files with 738 additions and 23 deletions

View File

@ -259,6 +259,10 @@ class PreferencesHelper(val context: Context) {
fun enableDoh() = prefs.getBoolean(Keys.enableDoh, false) fun enableDoh() = prefs.getBoolean(Keys.enableDoh, false)
fun lastSearchQuerySearchSettings() = prefs.getString("last_search_query", "")
fun lastSearchQuerySearchSettings(query: String) = prefs.edit { putString("last_search_query", query) }
fun filterChapterByRead() = prefs.getInt(Keys.defaultChapterFilterByRead, Manga.SHOW_ALL) fun filterChapterByRead() = prefs.getInt(Keys.defaultChapterFilterByRead, Manga.SHOW_ALL)
fun filterChapterByDownloaded() = prefs.getInt(Keys.defaultChapterFilterByDownloaded, Manga.SHOW_ALL) fun filterChapterByDownloaded() = prefs.getInt(Keys.defaultChapterFilterByDownloaded, Manga.SHOW_ALL)

View File

@ -13,7 +13,7 @@ import uy.kohesive.injekt.api.get
class ExtensionFilterController : SettingsController() { class ExtensionFilterController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.label_extensions titleRes = R.string.label_extensions
val activeLangs = preferences.enabledLanguages().get() val activeLangs = preferences.enabledLanguages().get()

View File

@ -45,7 +45,7 @@ class SourceFilterController : SettingsController() {
private var sourcesByLang: TreeMap<String, MutableList<HttpSource>> = TreeMap() private var sourcesByLang: TreeMap<String, MutableList<HttpSource>> = TreeMap()
private var sorting = SourcesSort.Alpha private var sorting = SourcesSort.Alpha
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.label_sources titleRes = R.string.label_sources
sorting = SourcesSort.from(preferences.sourceSorting().get()) ?: SourcesSort.Alpha sorting = SourcesSort.from(preferences.sourceSorting().get()) ?: SourcesSort.Alpha

View File

@ -43,10 +43,11 @@ class AboutController : SettingsController() {
private val isUpdaterEnabled = BuildConfig.INCLUDE_UPDATER private val isUpdaterEnabled = BuildConfig.INCLUDE_UPDATER
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_about titleRes = R.string.pref_category_about
preference { preference {
key = "pref_about_version"
titleRes = R.string.version titleRes = R.string.version
summary = if (BuildConfig.DEBUG /* SY --> */ || syDebugVersion != "0" /* SY --> */) { summary = if (BuildConfig.DEBUG /* SY --> */ || syDebugVersion != "0" /* SY --> */) {
"Preview r$syDebugVersion (${BuildConfig.COMMIT_SHA})" "Preview r$syDebugVersion (${BuildConfig.COMMIT_SHA})"
@ -57,17 +58,20 @@ class AboutController : SettingsController() {
onClick { copyDebugInfo() } onClick { copyDebugInfo() }
} }
preference { preference {
key = "pref_about_build_time"
titleRes = R.string.build_time titleRes = R.string.build_time
summary = getFormattedBuildTime() summary = getFormattedBuildTime()
} }
if (isUpdaterEnabled) { if (isUpdaterEnabled) {
preference { preference {
key = "pref_about_check_for_updates"
titleRes = R.string.check_for_updates titleRes = R.string.check_for_updates
onClick { checkVersion() } onClick { checkVersion() }
} }
} }
preference { preference {
key = "pref_about_whats_new"
titleRes = R.string.whats_new titleRes = R.string.whats_new
onClick { onClick {
@ -79,6 +83,7 @@ class AboutController : SettingsController() {
preferenceCategory { preferenceCategory {
preference { preference {
key = "pref_about_website"
titleRes = R.string.website titleRes = R.string.website
val url = "https://tachiyomi.org" val url = "https://tachiyomi.org"
summary = url summary = url
@ -88,6 +93,7 @@ class AboutController : SettingsController() {
} }
} }
preference { preference {
key = "pref_about_discord"
title = "Discord" title = "Discord"
val url = "https://discord.gg/tachiyomi" val url = "https://discord.gg/tachiyomi"
summary = url summary = url
@ -97,6 +103,7 @@ class AboutController : SettingsController() {
} }
} }
preference { preference {
key = "pref_about_github"
title = "GitHub" title = "GitHub"
// SY --> // SY -->
val url = "https://github.com/jobobby04/TachiyomiSY" val url = "https://github.com/jobobby04/TachiyomiSY"
@ -109,6 +116,7 @@ class AboutController : SettingsController() {
} }
// SY --> // SY -->
preference { preference {
key = "pref_about_label_original_tachiyomi_github"
title = "Original Tachiyomi GitHub " title = "Original Tachiyomi GitHub "
val url = "https://github.com/inorichi/tachiyomi" val url = "https://github.com/inorichi/tachiyomi"
summary = url summary = url
@ -119,6 +127,7 @@ class AboutController : SettingsController() {
} }
// SY <-- // SY <--
preference { preference {
key = "pref_about_label_extensions"
titleRes = R.string.label_extensions titleRes = R.string.label_extensions
val url = "https://github.com/inorichi/tachiyomi-extensions" val url = "https://github.com/inorichi/tachiyomi-extensions"
summary = url summary = url
@ -128,6 +137,7 @@ class AboutController : SettingsController() {
} }
} }
preference { preference {
key = "pref_about_licenses"
titleRes = R.string.licenses titleRes = R.string.licenses
onClick { onClick {

View File

@ -39,7 +39,7 @@ class MoreController :
private var isDownloading: Boolean = false private var isDownloading: Boolean = false
private var downloadQueueSize: Int = 0 private var downloadQueueSize: Int = 0
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.label_more titleRes = R.string.label_more
val tintColor = context.getResourceColor(R.attr.colorAccent) val tintColor = context.getResourceColor(R.attr.colorAccent)

View File

@ -58,7 +58,6 @@ import uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
class SettingsAdvancedController : SettingsController() { class SettingsAdvancedController : SettingsController() {
private val network: NetworkHelper by injectLazy() private val network: NetworkHelper by injectLazy()
private val chapterCache: ChapterCache by injectLazy() private val chapterCache: ChapterCache by injectLazy()
@ -66,11 +65,12 @@ class SettingsAdvancedController : SettingsController() {
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
@SuppressLint("BatteryLife") @SuppressLint("BatteryLife")
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_advanced titleRes = R.string.pref_category_advanced
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
preference { preference {
key = "pref_disable_battery_optimization"
titleRes = R.string.pref_disable_battery_optimization titleRes = R.string.pref_disable_battery_optimization
summaryRes = R.string.pref_disable_battery_optimization_summary summaryRes = R.string.pref_disable_battery_optimization_summary
@ -104,6 +104,7 @@ class SettingsAdvancedController : SettingsController() {
onClick { clearChapterCache() } onClick { clearChapterCache() }
} }
preference { preference {
key = "pref_clear_database"
titleRes = R.string.pref_clear_database titleRes = R.string.pref_clear_database
summaryRes = R.string.pref_clear_database_summary summaryRes = R.string.pref_clear_database_summary
@ -119,6 +120,7 @@ class SettingsAdvancedController : SettingsController() {
titleRes = R.string.label_network titleRes = R.string.label_network
preference { preference {
key = "pref_clear_cookies"
titleRes = R.string.pref_clear_cookies titleRes = R.string.pref_clear_cookies
onClick { onClick {
@ -138,11 +140,13 @@ class SettingsAdvancedController : SettingsController() {
titleRes = R.string.label_library titleRes = R.string.label_library
preference { preference {
key = "pref_refresh_library_covers"
titleRes = R.string.pref_refresh_library_covers titleRes = R.string.pref_refresh_library_covers
onClick { LibraryUpdateService.start(context, target = Target.COVERS) } onClick { LibraryUpdateService.start(context, target = Target.COVERS) }
} }
preference { preference {
key = "pref_refresh_library_tracking"
titleRes = R.string.pref_refresh_library_tracking titleRes = R.string.pref_refresh_library_tracking
summaryRes = R.string.pref_refresh_library_tracking_summary summaryRes = R.string.pref_refresh_library_tracking_summary
@ -155,6 +159,7 @@ class SettingsAdvancedController : SettingsController() {
titleRes = R.string.group_downloader titleRes = R.string.group_downloader
preference { preference {
key = "clean_up_downloaded_chapters"
titleRes = R.string.clean_up_downloaded_chapters titleRes = R.string.clean_up_downloaded_chapters
summaryRes = R.string.delete_unused_chapters summaryRes = R.string.delete_unused_chapters
@ -295,6 +300,7 @@ class SettingsAdvancedController : SettingsController() {
} }
preference { preference {
key = "pref_open_debug_menu"
titleRes = R.string.open_debug_menu titleRes = R.string.open_debug_menu
summary = HtmlCompat.fromHtml(context.getString(R.string.open_debug_menu_summary), HtmlCompat.FROM_HTML_MODE_LEGACY) summary = HtmlCompat.fromHtml(context.getString(R.string.open_debug_menu_summary), HtmlCompat.FROM_HTML_MODE_LEGACY)
onClick { router.pushController(SettingsDebugController().withFadeTransaction()) } onClick { router.pushController(SettingsDebugController().withFadeTransaction()) }

View File

@ -49,10 +49,11 @@ class SettingsBackupController : SettingsController() {
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 500) requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 500)
} }
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.backup titleRes = R.string.backup
preference { preference {
key = "pref_create_backup"
titleRes = R.string.pref_create_backup titleRes = R.string.pref_create_backup
summaryRes = R.string.pref_create_backup_summ summaryRes = R.string.pref_create_backup_summ
@ -67,6 +68,7 @@ class SettingsBackupController : SettingsController() {
} }
} }
preference { preference {
key = "pref_restore_backup"
titleRes = R.string.pref_restore_backup titleRes = R.string.pref_restore_backup
summaryRes = R.string.pref_restore_backup_summ summaryRes = R.string.pref_restore_backup_summ

View File

@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
class SettingsBrowseController : SettingsController() { class SettingsBrowseController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.browse titleRes = R.string.browse
// SY --> // SY -->
@ -25,6 +25,7 @@ class SettingsBrowseController : SettingsController() {
titleRes = R.string.label_sources titleRes = R.string.label_sources
preference { preference {
key = "pref_edit_source_categories"
titleRes = R.string.action_edit_categories titleRes = R.string.action_edit_categories
val catCount = preferences.sourcesTabCategories().get().count() val catCount = preferences.sourcesTabCategories().get().count()

View File

@ -1,6 +1,9 @@
package eu.kanade.tachiyomi.ui.setting package eu.kanade.tachiyomi.ui.setting
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.content.Context import android.content.Context
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue import android.util.TypedValue
import android.view.ContextThemeWrapper import android.view.ContextThemeWrapper
@ -9,12 +12,14 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceController import androidx.preference.PreferenceController
import androidx.preference.PreferenceGroup
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.util.system.getResourceColor
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -26,6 +31,7 @@ import uy.kohesive.injekt.api.get
abstract class SettingsController : PreferenceController() { abstract class SettingsController : PreferenceController() {
var preferenceKey: String? = null
val preferences: PreferencesHelper = Injekt.get() val preferences: PreferencesHelper = Injekt.get()
val scope = CoroutineScope(Job() + Dispatchers.Main) val scope = CoroutineScope(Job() + Dispatchers.Main)
@ -39,6 +45,24 @@ abstract class SettingsController : PreferenceController() {
return super.onCreateView(inflater, container, savedInstanceState) return super.onCreateView(inflater, container, savedInstanceState)
} }
override fun onAttach(view: View) {
super.onAttach(view)
preferenceKey?.let { prefKey ->
val adapter = listView.adapter
scrollToPreference(prefKey)
listView.post {
if (adapter is PreferenceGroup.PreferencePositionCallback) {
val pos = adapter.getPreferenceAdapterPosition(prefKey)
listView.findViewHolderForAdapterPosition(pos)?.let {
animatePreferenceHighlight(it.itemView)
}
}
}
}
}
override fun onDestroyView(view: View) { override fun onDestroyView(view: View) {
super.onDestroyView(view) super.onDestroyView(view)
untilDestroySubscriptions.unsubscribe() untilDestroySubscriptions.unsubscribe()
@ -50,7 +74,7 @@ abstract class SettingsController : PreferenceController() {
setupPreferenceScreen(screen) setupPreferenceScreen(screen)
} }
abstract fun setupPreferenceScreen(screen: PreferenceScreen): Any? abstract fun setupPreferenceScreen(screen: PreferenceScreen): PreferenceScreen
private fun getThemedContext(): Context { private fun getThemedContext(): Context {
val tv = TypedValue() val tv = TypedValue()
@ -58,6 +82,17 @@ abstract class SettingsController : PreferenceController() {
return ContextThemeWrapper(activity, tv.resourceId) return ContextThemeWrapper(activity, tv.resourceId)
} }
private fun animatePreferenceHighlight(view: View) {
ValueAnimator
.ofObject(ArgbEvaluator(), Color.TRANSPARENT, view.context.getResourceColor(R.attr.rippleColor))
.apply {
duration = 500L
repeatCount = 2
addUpdateListener { animator -> view.setBackgroundColor(animator.animatedValue as Int) }
reverse()
}
}
open fun getTitle(): String? { open fun getTitle(): String? {
return preferenceScreen?.title?.toString() return preferenceScreen?.title?.toString()
} }

View File

@ -40,7 +40,7 @@ class SettingsDownloadController : SettingsController() {
private val db: DatabaseHelper by injectLazy() private val db: DatabaseHelper by injectLazy()
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_downloads titleRes = R.string.pref_category_downloads
preference { preference {
@ -65,6 +65,7 @@ class SettingsDownloadController : SettingsController() {
defaultValue = true defaultValue = true
} }
// SY -->
switchPreference { switchPreference {
key = Keys.saveChaptersAsCBZ key = Keys.saveChaptersAsCBZ
titleRes = R.string.save_chapter_as_cbz titleRes = R.string.save_chapter_as_cbz
@ -81,6 +82,7 @@ class SettingsDownloadController : SettingsController() {
preferences.saveChaptersAsCBZ().asImmediateFlow { isVisible = it } preferences.saveChaptersAsCBZ().asImmediateFlow { isVisible = it }
.launchIn(scope) .launchIn(scope)
} }
// SY <--
preferenceCategory { preferenceCategory {
titleRes = R.string.pref_category_delete_chapters titleRes = R.string.pref_category_delete_chapters

View File

@ -144,7 +144,7 @@ class SettingsEhController : SettingsController() {
return true return true
} }
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_eh titleRes = R.string.pref_category_eh
preferenceCategory { preferenceCategory {
@ -214,6 +214,7 @@ class SettingsEhController : SettingsController() {
}.dependency = PreferenceKeys.eh_enableExHentai }.dependency = PreferenceKeys.eh_enableExHentai
preference { preference {
key = "pref_watched_tags"
titleRes = R.string.watched_tags titleRes = R.string.watched_tags
summaryRes = R.string.watched_tags_summary summaryRes = R.string.watched_tags_summary
onClick { onClick {
@ -300,6 +301,7 @@ class SettingsEhController : SettingsController() {
}.dependency = PreferenceKeys.eh_enableExHentai }.dependency = PreferenceKeys.eh_enableExHentai
preference { preference {
key = "pref_language_filtering"
titleRes = R.string.language_filtering titleRes = R.string.language_filtering
summaryRes = R.string.language_filtering_summary summaryRes = R.string.language_filtering_summary
@ -438,6 +440,7 @@ class SettingsEhController : SettingsController() {
}.dependency = PreferenceKeys.eh_enableExHentai }.dependency = PreferenceKeys.eh_enableExHentai
preference { preference {
key = "pref_front_page_categories"
titleRes = R.string.frong_page_categories titleRes = R.string.frong_page_categories
summaryRes = R.string.fromt_page_categories_summary summaryRes = R.string.fromt_page_categories_summary
@ -539,6 +542,7 @@ class SettingsEhController : SettingsController() {
} }
preference { preference {
key = "pref_show_sync_favorite_notes"
titleRes = R.string.show_favorite_sync_notes titleRes = R.string.show_favorite_sync_notes
summaryRes = R.string.show_favorite_sync_notes_summary summaryRes = R.string.show_favorite_sync_notes_summary
@ -557,6 +561,7 @@ class SettingsEhController : SettingsController() {
} }
preference { preference {
key = "pref_force_sync_reset"
titleRes = R.string.force_sync_state_reset titleRes = R.string.force_sync_state_reset
summaryRes = R.string.force_sync_state_reset_summary summaryRes = R.string.force_sync_state_reset_summary
@ -638,6 +643,7 @@ class SettingsEhController : SettingsController() {
} }
preference { preference {
key = "pref_show_updater_statistics"
titleRes = R.string.show_updater_statistics titleRes = R.string.show_updater_statistics
onClick { onClick {

View File

@ -25,7 +25,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
class SettingsGeneralController : SettingsController() { class SettingsGeneralController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_general titleRes = R.string.pref_category_general
intListPreference { intListPreference {
@ -48,6 +48,7 @@ class SettingsGeneralController : SettingsController() {
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
preference { preference {
key = "pref_manage_notifications"
titleRes = R.string.pref_manage_notifications titleRes = R.string.pref_manage_notifications
onClick { onClick {
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {

View File

@ -49,17 +49,17 @@ class SettingsLibraryController : SettingsController() {
*/ */
private var settingsSheet: LibrarySettingsSheet? = null private var settingsSheet: LibrarySettingsSheet? = null
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_library titleRes = R.string.pref_category_library
val dbCategories = db.getCategories().executeAsBlocking() val dbCategories = db.getCategories().executeAsBlocking()
val categories = listOf(Category.createDefault()) + dbCategories val categories = listOf(Category.createDefault()) + dbCategories
settingsSheet = LibrarySettingsSheet(router) {}
preferenceCategory { preferenceCategory {
titleRes = R.string.pref_category_display titleRes = R.string.pref_category_display
preference { preference {
key = "pref_library_columns"
titleRes = R.string.pref_library_columns titleRes = R.string.pref_library_columns
onClick { onClick {
LibraryColumnsDialog().showDialog(router) LibraryColumnsDialog().showDialog(router)
@ -89,11 +89,15 @@ class SettingsLibraryController : SettingsController() {
} }
// SY --> // SY -->
preference { preference {
key = "pref_library_settings_sheet"
titleRes = R.string.library_settings_sheet titleRes = R.string.library_settings_sheet
summaryRes = R.string.library_settings_sheet_summary summaryRes = R.string.library_settings_sheet_summary
onClick { onClick {
if (settingsSheet == null) {
settingsSheet = LibrarySettingsSheet(router) {}
}
settingsSheet?.show() settingsSheet?.show()
} }
} }
@ -104,6 +108,7 @@ class SettingsLibraryController : SettingsController() {
titleRes = R.string.pref_category_library_categories titleRes = R.string.pref_category_library_categories
preference { preference {
key = "pref_action_edit_categories"
titleRes = R.string.action_edit_categories titleRes = R.string.action_edit_categories
val catCount = dbCategories.size val catCount = dbCategories.size
@ -258,19 +263,20 @@ class SettingsLibraryController : SettingsController() {
} }
} }
// SY -->
preferenceCategory { preferenceCategory {
titleRes = R.string.pref_sorting_settings titleRes = R.string.pref_sorting_settings
preference { preference {
key = "pref_tag_sorting"
titleRes = R.string.pref_tag_sorting titleRes = R.string.pref_tag_sorting
val count = preferences.sortTagsForLibrary().get().size val count = preferences.sortTagsForLibrary().get().size
summary = resources!!.getQuantityString(R.plurals.pref_tag_sorting_desc, count, count) summary = context.resources.getQuantityString(R.plurals.pref_tag_sorting_desc, count, count)
onClick { onClick {
router.pushController(SortTagController().withFadeTransaction()) router.pushController(SortTagController().withFadeTransaction())
} }
} }
} }
// SY -->
if (preferences.skipPreMigration().get() || preferences.migrationSources().get() if (preferences.skipPreMigration().get() || preferences.migrationSources().get()
.isNotEmpty() .isNotEmpty()
) { ) {

View File

@ -1,8 +1,13 @@
package eu.kanade.tachiyomi.ui.setting package eu.kanade.tachiyomi.ui.setting
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.appcompat.widget.SearchView
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.setting.settingssearch.SettingsSearchController
import eu.kanade.tachiyomi.util.preference.iconRes import eu.kanade.tachiyomi.util.preference.iconRes
import eu.kanade.tachiyomi.util.preference.iconTint import eu.kanade.tachiyomi.util.preference.iconTint
import eu.kanade.tachiyomi.util.preference.onClick import eu.kanade.tachiyomi.util.preference.onClick
@ -13,7 +18,7 @@ import exh.md.utils.MdUtil
class SettingsMainController : SettingsController() { class SettingsMainController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.label_settings titleRes = R.string.label_settings
val tintColor = context.getResourceColor(R.attr.colorAccent) val tintColor = context.getResourceColor(R.attr.colorAccent)
@ -101,4 +106,31 @@ class SettingsMainController : SettingsController() {
private fun navigateTo(controller: SettingsController) { private fun navigateTo(controller: SettingsController) {
router.pushController(controller.withFadeTransaction()) router.pushController(controller.withFadeTransaction())
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
// Inflate menu
inflater.inflate(R.menu.settings_main, menu)
// Initialize search option.
val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem.actionView as SearchView
searchView.maxWidth = Int.MAX_VALUE
// Change hint to show global search.
searchView.queryHint = applicationContext?.getString(R.string.action_search_settings)
searchItem.setOnActionExpandListener(
object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
preferences.lastSearchQuerySearchSettings("") // reset saved search query
router.pushController(SettingsSearchController().withFadeTransaction())
return true
}
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
return true
}
}
)
}
} }

View File

@ -24,7 +24,7 @@ class SettingsMangaDexController :
private val mdex by lazy { MdUtil.getEnabledMangaDex(preferences) } private val mdex by lazy { MdUtil.getEnabledMangaDex(preferences) }
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.mangadex_specific_settings titleRes = R.string.mangadex_specific_settings
if (mdex == null) router.popCurrentController() if (mdex == null) router.popCurrentController()
val sourcePreference = MangaDexLoginPreference(context, mdex!!).apply { val sourcePreference = MangaDexLoginPreference(context, mdex!!).apply {
@ -68,6 +68,7 @@ class SettingsMangaDexController :
} }
preference { preference {
key = "pref_sync_mangadex_into_this"
titleRes = R.string.mangadex_sync_follows_to_library titleRes = R.string.mangadex_sync_follows_to_library
summaryRes = R.string.mangadex_sync_follows_to_library_summary summaryRes = R.string.mangadex_sync_follows_to_library_summary

View File

@ -13,7 +13,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
class SettingsParentalControlsController : SettingsController() { class SettingsParentalControlsController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_parental_controls titleRes = R.string.pref_category_parental_controls
listPreference { listPreference {

View File

@ -19,7 +19,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
class SettingsReaderController : SettingsController() { class SettingsReaderController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_reader titleRes = R.string.pref_category_reader
intListPreference { intListPreference {

View File

@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
class SettingsSecurityController : SettingsController() { class SettingsSecurityController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_security titleRes = R.string.pref_category_security
if (BiometricManager.from(context).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) { if (BiometricManager.from(context).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
@ -59,6 +59,7 @@ class SettingsSecurityController : SettingsController() {
defaultValue = false defaultValue = false
} }
preference { preference {
key = "pref_edit_lock_times"
titleRes = R.string.action_edit_biometric_lock_times titleRes = R.string.action_edit_biometric_lock_times
val timeRanges = preferences.biometricTimeRanges().get().count() val timeRanges = preferences.biometricTimeRanges().get().count()

View File

@ -31,7 +31,7 @@ class SettingsTrackingController :
private val trackManager: TrackManager by injectLazy() private val trackManager: TrackManager by injectLazy()
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.pref_category_tracking titleRes = R.string.pref_category_tracking
switchPreference { switchPreference {

View File

@ -0,0 +1,80 @@
package eu.kanade.tachiyomi.ui.setting.settingssearch
import android.os.Bundle
import android.os.Parcelable
import android.util.SparseArray
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.ui.setting.SettingsController
/**
* Adapter that holds the search cards.
*
* @param controller instance of [SettingsSearchController].
*/
class SettingsSearchAdapter(val controller: SettingsSearchController) :
FlexibleAdapter<SettingsSearchItem>(null, controller, true) {
val titleClickListener: OnTitleClickListener = controller
/**
* Bundle where the view state of the holders is saved.
*/
private var bundle = Bundle()
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: List<Any?>) {
super.onBindViewHolder(holder, position, payloads)
restoreHolderState(holder)
}
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
super.onViewRecycled(holder)
saveHolderState(holder, bundle)
}
override fun onSaveInstanceState(outState: Bundle) {
val holdersBundle = Bundle()
allBoundViewHolders.forEach { saveHolderState(it, holdersBundle) }
outState.putBundle(HOLDER_BUNDLE_KEY, holdersBundle)
super.onSaveInstanceState(outState)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
bundle = savedInstanceState.getBundle(HOLDER_BUNDLE_KEY)!!
}
/**
* Saves the view state of the given holder.
*
* @param holder The holder to save.
* @param outState The bundle where the state is saved.
*/
private fun saveHolderState(holder: RecyclerView.ViewHolder, outState: Bundle) {
val key = "holder_${holder.bindingAdapterPosition}"
val holderState = SparseArray<Parcelable>()
holder.itemView.saveHierarchyState(holderState)
outState.putSparseParcelableArray(key, holderState)
}
/**
* Restores the view state of the given holder.
*
* @param holder The holder to restore.
*/
private fun restoreHolderState(holder: RecyclerView.ViewHolder) {
val key = "holder_${holder.bindingAdapterPosition}"
bundle.getSparseParcelableArray<Parcelable>(key)?.let {
holder.itemView.restoreHierarchyState(it)
bundle.remove(key)
}
}
interface OnTitleClickListener {
fun onTitleClick(ctrl: SettingsController)
}
private companion object {
const val HOLDER_BUNDLE_KEY = "holder_bundle"
}
}

View File

@ -0,0 +1,172 @@
package eu.kanade.tachiyomi.ui.setting.settingssearch
import android.os.Bundle
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 androidx.recyclerview.widget.LinearLayoutManager
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.SettingsSearchControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.setting.SettingsController
/**
* This controller shows and manages the different search result in settings search.
* [SettingsSearchAdapter.OnTitleClickListener] called when preference is clicked in settings search
*/
class SettingsSearchController :
NucleusController<SettingsSearchControllerBinding, SettingsSearchPresenter>(),
SettingsSearchAdapter.OnTitleClickListener {
/**
* Adapter containing search results grouped by lang.
*/
protected var adapter: SettingsSearchAdapter? = null
lateinit var searchView: SearchView
init {
setHasOptionsMenu(true)
}
/**
* Initiate the view with [R.layout.settings_search_controller].
*
* @param inflater used to load the layout xml.
* @param container containing parent views.
* @return inflated view
*/
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = SettingsSearchControllerBinding.inflate(inflater)
return binding.root
}
override fun getTitle(): String? {
return presenter.query
}
/**
* Create the [SettingsSearchPresenter] used in controller.
*
* @return instance of [SettingsSearchPresenter]
*/
override fun createPresenter(): SettingsSearchPresenter {
return SettingsSearchPresenter()
}
/**
* 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) {
// Inflate menu.
inflater.inflate(R.menu.settings_main, menu)
// Initialize search menu
val searchItem = menu.findItem(R.id.action_search)
searchView = searchItem.actionView as SearchView
searchView.maxWidth = Int.MAX_VALUE
// Change hint to show "search settings."
searchView.queryHint = applicationContext?.getString(R.string.action_search_settings)
searchItem.expandActionView()
setItems(getResultSet())
searchItem.setOnActionExpandListener(
object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
return true
}
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
router.popCurrentController()
return false
}
}
)
searchView.setOnQueryTextListener(
object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
setItems(getResultSet(query))
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
setItems(getResultSet(newText))
return false
}
}
)
searchView.setQuery(presenter.preferences.lastSearchQuerySearchSettings(), true)
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
adapter = SettingsSearchAdapter(this)
// Create recycler and set adapter.
binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.adapter = adapter
// load all search results
SettingsSearchHelper.initPreferenceSearchResultCollection(presenter.preferences.context)
}
override fun onDestroyView(view: View) {
adapter = null
super.onDestroyView(view)
}
override fun onSaveViewState(view: View, outState: Bundle) {
super.onSaveViewState(view, outState)
adapter?.onSaveInstanceState(outState)
}
override fun onRestoreViewState(view: View, savedViewState: Bundle) {
super.onRestoreViewState(view, savedViewState)
adapter?.onRestoreInstanceState(savedViewState)
}
/**
* returns a list of `SettingsSearchItem` to be shown as search results
* Future update: should we add a minimum length to the query before displaying results? Consider other languages.
*/
fun getResultSet(query: String? = null): List<SettingsSearchItem> {
if (!query.isNullOrBlank()) {
return SettingsSearchHelper.getFilteredResults(query)
.map { SettingsSearchItem(it, null) }
}
return mutableListOf()
}
/**
* Add search result to adapter.
*
* @param searchResult result of search.
*/
fun setItems(searchResult: List<SettingsSearchItem>) {
adapter?.updateDataSet(searchResult)
}
/**
* Opens a catalogue with the given search.
*/
override fun onTitleClick(ctrl: SettingsController) {
searchView.query.let {
presenter.preferences.lastSearchQuerySearchSettings(it.toString())
}
router.pushController(ctrl.withFadeTransaction())
}
}

View File

@ -0,0 +1,136 @@
package eu.kanade.tachiyomi.ui.setting.settingssearch
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceGroup
import androidx.preference.PreferenceManager
import eu.kanade.tachiyomi.ui.setting.SettingsAdvancedController
import eu.kanade.tachiyomi.ui.setting.SettingsBackupController
import eu.kanade.tachiyomi.ui.setting.SettingsBrowseController
import eu.kanade.tachiyomi.ui.setting.SettingsController
import eu.kanade.tachiyomi.ui.setting.SettingsDownloadController
import eu.kanade.tachiyomi.ui.setting.SettingsGeneralController
import eu.kanade.tachiyomi.ui.setting.SettingsLibraryController
import eu.kanade.tachiyomi.ui.setting.SettingsParentalControlsController
import eu.kanade.tachiyomi.ui.setting.SettingsReaderController
import eu.kanade.tachiyomi.ui.setting.SettingsSecurityController
import eu.kanade.tachiyomi.ui.setting.SettingsTrackingController
import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.system.isLTR
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
object SettingsSearchHelper {
var prefSearchResultList: MutableList<SettingsSearchResult> = mutableListOf()
private set
/**
* All subclasses of `SettingsController` should be listed here, in order to have their preferences searchable.
*/
private val settingControllersList: List<KClass<out SettingsController>> = listOf(
SettingsAdvancedController::class,
SettingsBackupController::class,
SettingsBrowseController::class,
SettingsDownloadController::class,
SettingsGeneralController::class,
SettingsLibraryController::class,
SettingsParentalControlsController::class,
SettingsReaderController::class,
SettingsSecurityController::class,
SettingsTrackingController::class
)
/**
* Must be called to populate `prefSearchResultList`
*/
@SuppressLint("RestrictedApi")
fun initPreferenceSearchResultCollection(context: Context) {
val preferenceManager = PreferenceManager(context)
prefSearchResultList.clear()
launchNow {
settingControllersList.forEach { kClass ->
val ctrl = kClass.createInstance()
val settingsPrefScreen = ctrl.setupPreferenceScreen(preferenceManager.createPreferenceScreen(context))
val prefCount = settingsPrefScreen.preferenceCount
for (i in 0 until prefCount) {
val rootPref = settingsPrefScreen.getPreference(i)
if (rootPref.title == null) continue // no title, not a preference. (note: only info notes appear to not have titles)
getSettingSearchResult(ctrl, rootPref, "${settingsPrefScreen.title}")
}
}
}
}
fun getFilteredResults(query: String): List<SettingsSearchResult> {
return prefSearchResultList.filter {
val inTitle = it.title.contains(query, true)
val inSummary = it.summary.contains(query, true)
val inBreadcrumb = it.breadcrumb.contains(query, true)
return@filter inTitle || inSummary || inBreadcrumb
}
}
/**
* Extracts the data needed from a `Preference` to create a `SettingsSearchResult`, and then adds it to `prefSearchResultList`
* Future enhancement: make bold the text matched by the search query.
*/
private fun getSettingSearchResult(ctrl: SettingsController, pref: Preference, breadcrumbs: String = "") {
when (pref) {
is PreferenceGroup -> {
val breadcrumbsStr = addLocalizedBreadcrumb(breadcrumbs, "${pref.title}")
for (x in 0 until pref.preferenceCount) {
val subPref = pref.getPreference(x)
getSettingSearchResult(ctrl, subPref, breadcrumbsStr) // recursion
}
}
is PreferenceCategory -> {
val breadcrumbsStr = addLocalizedBreadcrumb(breadcrumbs, "${pref.title}")
for (x in 0 until pref.preferenceCount) {
val subPref = pref.getPreference(x)
getSettingSearchResult(ctrl, subPref, breadcrumbsStr) // recursion
}
}
else -> {
// Is an actual preference
val title = pref.title.toString()
val summary = if (pref.summary != null) pref.summary.toString() else ""
val breadcrumbsStr = addLocalizedBreadcrumb(breadcrumbs, "${pref.title}")
prefSearchResultList.add(
SettingsSearchResult(
key = pref.key,
title = title,
summary = summary,
breadcrumb = breadcrumbsStr,
searchController = ctrl
)
)
}
}
}
private fun addLocalizedBreadcrumb(path: String, node: String): String {
return if (Resources.getSystem().isLTR) {
// This locale reads left to right.
"$path > $node"
} else {
// This locale reads right to left.
"$node < $path"
}
}
data class SettingsSearchResult(
val key: String?,
val title: String,
val summary: String,
val breadcrumb: String,
val searchController: SettingsController
)
}

View File

@ -0,0 +1,42 @@
package eu.kanade.tachiyomi.ui.setting.settingssearch
import android.view.View
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import kotlinx.android.synthetic.main.settings_search_controller_card.search_result_pref_breadcrumb
import kotlinx.android.synthetic.main.settings_search_controller_card.search_result_pref_summary
import kotlinx.android.synthetic.main.settings_search_controller_card.search_result_pref_title
import kotlinx.android.synthetic.main.settings_search_controller_card.title_wrapper
import kotlin.reflect.full.createInstance
/**
* Holder that binds the [SettingsSearchItem] containing catalogue cards.
*
* @param view view of [SettingsSearchItem]
* @param adapter instance of [SettingsSearchAdapter]
*/
class SettingsSearchHolder(view: View, val adapter: SettingsSearchAdapter) :
BaseFlexibleViewHolder(view, adapter) {
init {
title_wrapper.setOnClickListener {
adapter.getItem(bindingAdapterPosition)?.let {
val ctrl = it.settingsSearchResult.searchController::class.createInstance()
ctrl.preferenceKey = it.settingsSearchResult.key
// must pass a new Controller instance to avoid this error https://github.com/bluelinelabs/Conductor/issues/446
adapter.titleClickListener.onTitleClick(ctrl)
}
}
}
/**
* Show the loading of source search result.
*
* @param item item of card.
*/
fun bind(item: SettingsSearchItem) {
search_result_pref_title.text = item.settingsSearchResult.title
search_result_pref_summary.text = item.settingsSearchResult.summary
search_result_pref_breadcrumb.text = item.settingsSearchResult.breadcrumb
}
}

View File

@ -0,0 +1,51 @@
package eu.kanade.tachiyomi.ui.setting.settingssearch
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
/**
* Item that contains search result information.
*
* @param pref the source for the search results.
* @param results the search results.
*/
class SettingsSearchItem(val settingsSearchResult: SettingsSearchHelper.SettingsSearchResult, val results: List<SettingsSearchItem>?) :
AbstractFlexibleItem<SettingsSearchHolder>() {
override fun getLayoutRes(): Int {
return R.layout.settings_search_controller_card
}
/**
* Create view holder (see [SettingsSearchAdapter].
*
* @return holder of view.
*/
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): SettingsSearchHolder {
return SettingsSearchHolder(view, adapter as SettingsSearchAdapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: SettingsSearchHolder,
position: Int,
payloads: List<Any?>?
) {
holder.bind(this)
}
override fun equals(other: Any?): Boolean {
if (other is SettingsSearchItem) {
return settingsSearchResult == settingsSearchResult
}
return false
}
override fun hashCode(): Int {
return settingsSearchResult.hashCode()
}
}

View File

@ -0,0 +1,32 @@
package eu.kanade.tachiyomi.ui.setting.settingssearch
import android.os.Bundle
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* Presenter of [SettingsSearchController]
* Function calls should be done from here. UI calls should be done from the controller.
*/
open class SettingsSearchPresenter : BasePresenter<SettingsSearchController>() {
/**
* Query from the view.
*/
var query = ""
private set
val preferences: PreferencesHelper = Injekt.get()
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
query = savedState?.getString(SettingsSearchPresenter::query.name) ?: "" // TODO - Some way to restore previous query?
}
override fun onSave(state: Bundle) {
state.putString(SettingsSearchPresenter::query.name, query)
super.onSave(state)
}
}

View File

@ -20,7 +20,7 @@ import kotlin.reflect.full.declaredFunctions
class SettingsDebugController : SettingsController() { class SettingsDebugController : SettingsController() {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
title = "DEBUG MENU" title = "DEBUG MENU"
preferenceCategory { preferenceCategory {

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingTop="4dp"
android:paddingBottom="4dp"
tools:listitem="@layout/settings_search_controller_card" />
<FrameLayout
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
android:alpha="0.75" />
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center" />
</FrameLayout>
</FrameLayout>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/title_wrapper"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="48dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="?attr/selectableItemBackground">
<TextView
android:id="@+id/search_result_pref_title"
style="@style/TextAppearance.Regular.SubHeading"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
<TextView
android:id="@+id/search_result_pref_summary"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search_result_pref_title"
tools:text="Summary" />
<TextView
android:id="@+id/search_result_pref_breadcrumb"
style="@style/TextAppearance.Regular.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search_result_pref_summary"
tools:text="Location" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:icon="@drawable/ic_search_24dp"
android:title="@string/action_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:iconTint="?attr/colorOnPrimary"
app:showAsAction="collapseActionView|ifRoom" />
</menu>

View File

@ -44,6 +44,7 @@
<string name="action_sort_latest_chapter">Latest chapter</string> <string name="action_sort_latest_chapter">Latest chapter</string>
<string name="action_sort_date_added">Date added</string> <string name="action_sort_date_added">Date added</string>
<string name="action_search">Search</string> <string name="action_search">Search</string>
<string name="action_search_settings">Search settings</string>
<string name="action_global_search">Global search</string> <string name="action_global_search">Global search</string>
<string name="action_select_all">Select all</string> <string name="action_select_all">Select all</string>
<string name="action_select_inverse">Select inverse</string> <string name="action_select_inverse">Select inverse</string>
@ -412,6 +413,7 @@
<string name="licenses">Open source licenses</string> <string name="licenses">Open source licenses</string>
<string name="check_for_updates">Check for updates</string> <string name="check_for_updates">Check for updates</string>
<string name="updated_version">Updated to v%1$s</string> <string name="updated_version">Updated to v%1$s</string>
<string name="about_resources">Resources</string>
<!-- ACRA --> <!-- ACRA -->
<string name="pref_enable_acra">Send crash reports</string> <string name="pref_enable_acra">Send crash reports</string>