mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	* 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>
This commit is contained in:
		| @@ -253,6 +253,10 @@ class PreferencesHelper(val context: Context) { | ||||
|  | ||||
|     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 filterChapterByDownloaded() = prefs.getInt(Keys.defaultChapterFilterByDownloaded, Manga.SHOW_ALL) | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import uy.kohesive.injekt.api.get | ||||
|  | ||||
| class ExtensionFilterController : SettingsController() { | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.label_extensions | ||||
|  | ||||
|         val activeLangs = preferences.enabledLanguages().get() | ||||
|   | ||||
| @@ -24,7 +24,7 @@ class SourceFilterController : SettingsController() { | ||||
|  | ||||
|     private val onlineSources by lazy { Injekt.get<SourceManager>().getOnlineSources() } | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.label_sources | ||||
|  | ||||
|         // Get the list of active language codes. | ||||
| @@ -46,7 +46,7 @@ class SourceFilterController : SettingsController() { | ||||
|  | ||||
|             // Create a preference group and set initial state and change listener | ||||
|             switchPreferenceCategory { | ||||
|                 preferenceScreen.addPreference(this) | ||||
|                 this@apply.addPreference(this) | ||||
|                 title = LocaleHelper.getSourceDisplayName(lang, context) | ||||
|                 isPersistent = false | ||||
|                 if (lang in activeLangsCodes) { | ||||
|   | ||||
| @@ -41,10 +41,11 @@ class AboutController : SettingsController() { | ||||
|  | ||||
|     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 | ||||
|  | ||||
|         preference { | ||||
|             key = "pref_about_version" | ||||
|             titleRes = R.string.version | ||||
|             summary = if (BuildConfig.DEBUG) { | ||||
|                 "Preview r${BuildConfig.COMMIT_COUNT} (${BuildConfig.COMMIT_SHA})" | ||||
| @@ -55,17 +56,20 @@ class AboutController : SettingsController() { | ||||
|             onClick { copyDebugInfo() } | ||||
|         } | ||||
|         preference { | ||||
|             key = "pref_about_build_time" | ||||
|             titleRes = R.string.build_time | ||||
|             summary = getFormattedBuildTime() | ||||
|         } | ||||
|         if (isUpdaterEnabled) { | ||||
|             preference { | ||||
|                 key = "pref_about_check_for_updates" | ||||
|                 titleRes = R.string.check_for_updates | ||||
|  | ||||
|                 onClick { checkVersion() } | ||||
|             } | ||||
|         } | ||||
|         preference { | ||||
|             key = "pref_about_whats_new" | ||||
|             titleRes = R.string.whats_new | ||||
|  | ||||
|             onClick { | ||||
| @@ -81,6 +85,7 @@ class AboutController : SettingsController() { | ||||
|         } | ||||
|         if (BuildConfig.DEBUG) { | ||||
|             preference { | ||||
|                 key = "pref_about_notices" | ||||
|                 titleRes = R.string.notices | ||||
|  | ||||
|                 onClick { | ||||
| @@ -92,6 +97,7 @@ class AboutController : SettingsController() { | ||||
|  | ||||
|         preferenceCategory { | ||||
|             preference { | ||||
|                 key = "pref_about_website" | ||||
|                 titleRes = R.string.website | ||||
|                 val url = "https://tachiyomi.org" | ||||
|                 summary = url | ||||
| @@ -101,6 +107,7 @@ class AboutController : SettingsController() { | ||||
|                 } | ||||
|             } | ||||
|             preference { | ||||
|                 key = "pref_about_discord" | ||||
|                 title = "Discord" | ||||
|                 val url = "https://discord.gg/tachiyomi" | ||||
|                 summary = url | ||||
| @@ -110,6 +117,7 @@ class AboutController : SettingsController() { | ||||
|                 } | ||||
|             } | ||||
|             preference { | ||||
|                 key = "pref_about_github" | ||||
|                 title = "GitHub" | ||||
|                 val url = "https://github.com/inorichi/tachiyomi" | ||||
|                 summary = url | ||||
| @@ -119,6 +127,7 @@ class AboutController : SettingsController() { | ||||
|                 } | ||||
|             } | ||||
|             preference { | ||||
|                 key = "pref_about_label_extensions" | ||||
|                 titleRes = R.string.label_extensions | ||||
|                 val url = "https://github.com/inorichi/tachiyomi-extensions" | ||||
|                 summary = url | ||||
| @@ -128,6 +137,7 @@ class AboutController : SettingsController() { | ||||
|                 } | ||||
|             } | ||||
|             preference { | ||||
|                 key = "pref_about_licenses" | ||||
|                 titleRes = R.string.licenses | ||||
|  | ||||
|                 onClick { | ||||
|   | ||||
| @@ -38,7 +38,7 @@ class MoreController : | ||||
|     private var isDownloading: Boolean = false | ||||
|     private var downloadQueueSize: Int = 0 | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.label_more | ||||
|  | ||||
|         val tintColor = context.getResourceColor(R.attr.colorAccent) | ||||
|   | ||||
| @@ -33,7 +33,6 @@ import uy.kohesive.injekt.injectLazy | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
|  | ||||
| class SettingsAdvancedController : SettingsController() { | ||||
|  | ||||
|     private val network: NetworkHelper by injectLazy() | ||||
|  | ||||
|     private val chapterCache: ChapterCache by injectLazy() | ||||
| @@ -41,9 +40,8 @@ class SettingsAdvancedController : SettingsController() { | ||||
|     private val db: DatabaseHelper by injectLazy() | ||||
|  | ||||
|     @SuppressLint("BatteryLife") | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.pref_category_advanced | ||||
|  | ||||
|         switchPreference { | ||||
|             key = "acra.enable" | ||||
|             titleRes = R.string.pref_enable_acra | ||||
| @@ -53,6 +51,7 @@ class SettingsAdvancedController : SettingsController() { | ||||
|  | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||||
|             preference { | ||||
|                 key = "pref_disable_battery_optimization" | ||||
|                 titleRes = R.string.pref_disable_battery_optimization | ||||
|                 summaryRes = R.string.pref_disable_battery_optimization_summary | ||||
|  | ||||
| @@ -86,6 +85,7 @@ class SettingsAdvancedController : SettingsController() { | ||||
|                 onClick { clearChapterCache() } | ||||
|             } | ||||
|             preference { | ||||
|                 key = "pref_clear_database" | ||||
|                 titleRes = R.string.pref_clear_database | ||||
|                 summaryRes = R.string.pref_clear_database_summary | ||||
|  | ||||
| @@ -101,6 +101,7 @@ class SettingsAdvancedController : SettingsController() { | ||||
|             titleRes = R.string.label_network | ||||
|  | ||||
|             preference { | ||||
|                 key = "pref_clear_cookies" | ||||
|                 titleRes = R.string.pref_clear_cookies | ||||
|  | ||||
|                 onClick { | ||||
| @@ -120,11 +121,13 @@ class SettingsAdvancedController : SettingsController() { | ||||
|             titleRes = R.string.label_library | ||||
|  | ||||
|             preference { | ||||
|                 key = "pref_refresh_library_covers" | ||||
|                 titleRes = R.string.pref_refresh_library_covers | ||||
|  | ||||
|                 onClick { LibraryUpdateService.start(context, target = Target.COVERS) } | ||||
|             } | ||||
|             preference { | ||||
|                 key = "pref_refresh_library_tracking" | ||||
|                 titleRes = R.string.pref_refresh_library_tracking | ||||
|                 summaryRes = R.string.pref_refresh_library_tracking_summary | ||||
|  | ||||
|   | ||||
| @@ -49,10 +49,11 @@ class SettingsBackupController : SettingsController() { | ||||
|         requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 500) | ||||
|     } | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.backup | ||||
|  | ||||
|         preference { | ||||
|             key = "pref_create_backup" | ||||
|             titleRes = R.string.pref_create_backup | ||||
|             summaryRes = R.string.pref_create_backup_summ | ||||
|  | ||||
| @@ -67,6 +68,7 @@ class SettingsBackupController : SettingsController() { | ||||
|             } | ||||
|         } | ||||
|         preference { | ||||
|             key = "pref_restore_backup" | ||||
|             titleRes = R.string.pref_restore_backup | ||||
|             summaryRes = R.string.pref_restore_backup_summ | ||||
|  | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
|  | ||||
| class SettingsBrowseController : SettingsController() { | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.browse | ||||
|  | ||||
|         preferenceCategory { | ||||
|   | ||||
| @@ -1,6 +1,9 @@ | ||||
| package eu.kanade.tachiyomi.ui.setting | ||||
|  | ||||
| import android.animation.ArgbEvaluator | ||||
| import android.animation.ValueAnimator | ||||
| import android.content.Context | ||||
| import android.graphics.Color | ||||
| import android.os.Bundle | ||||
| import android.util.TypedValue | ||||
| import android.view.ContextThemeWrapper | ||||
| @@ -9,12 +12,14 @@ import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.preference.PreferenceController | ||||
| import androidx.preference.PreferenceGroup | ||||
| import androidx.preference.PreferenceScreen | ||||
| import com.bluelinelabs.conductor.ControllerChangeHandler | ||||
| import com.bluelinelabs.conductor.ControllerChangeType | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.ui.base.controller.BaseController | ||||
| import eu.kanade.tachiyomi.util.system.getResourceColor | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.Job | ||||
| @@ -26,6 +31,7 @@ import uy.kohesive.injekt.api.get | ||||
|  | ||||
| abstract class SettingsController : PreferenceController() { | ||||
|  | ||||
|     var preferenceKey: String? = null | ||||
|     val preferences: PreferencesHelper = Injekt.get() | ||||
|     val scope = CoroutineScope(Job() + Dispatchers.Main) | ||||
|  | ||||
| @@ -39,6 +45,24 @@ abstract class SettingsController : PreferenceController() { | ||||
|         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) { | ||||
|         super.onDestroyView(view) | ||||
|         untilDestroySubscriptions.unsubscribe() | ||||
| @@ -50,7 +74,7 @@ abstract class SettingsController : PreferenceController() { | ||||
|         setupPreferenceScreen(screen) | ||||
|     } | ||||
|  | ||||
|     abstract fun setupPreferenceScreen(screen: PreferenceScreen): Any? | ||||
|     abstract fun setupPreferenceScreen(screen: PreferenceScreen): PreferenceScreen | ||||
|  | ||||
|     private fun getThemedContext(): Context { | ||||
|         val tv = TypedValue() | ||||
| @@ -58,6 +82,17 @@ abstract class SettingsController : PreferenceController() { | ||||
|         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? { | ||||
|         return preferenceScreen?.title?.toString() | ||||
|     } | ||||
|   | ||||
| @@ -40,7 +40,7 @@ class SettingsDownloadController : SettingsController() { | ||||
|  | ||||
|     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 | ||||
|  | ||||
|         preference { | ||||
|   | ||||
| @@ -24,7 +24,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values | ||||
|  | ||||
| class SettingsGeneralController : SettingsController() { | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.pref_category_general | ||||
|  | ||||
|         intListPreference { | ||||
| @@ -47,6 +47,7 @@ class SettingsGeneralController : SettingsController() { | ||||
|         } | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|             preference { | ||||
|                 key = "pref_manage_notifications" | ||||
|                 titleRes = R.string.pref_manage_notifications | ||||
|                 onClick { | ||||
|                     val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { | ||||
|   | ||||
| @@ -40,7 +40,7 @@ class SettingsLibraryController : SettingsController() { | ||||
|  | ||||
|     private val db: DatabaseHelper = Injekt.get() | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.pref_category_library | ||||
|  | ||||
|         val dbCategories = db.getCategories().executeAsBlocking() | ||||
| @@ -50,6 +50,7 @@ class SettingsLibraryController : SettingsController() { | ||||
|             titleRes = R.string.pref_category_display | ||||
|  | ||||
|             preference { | ||||
|                 key = "pref_library_columns" | ||||
|                 titleRes = R.string.pref_library_columns | ||||
|                 onClick { | ||||
|                     LibraryColumnsDialog().showDialog(router) | ||||
| @@ -83,6 +84,7 @@ class SettingsLibraryController : SettingsController() { | ||||
|             titleRes = R.string.pref_category_library_categories | ||||
|  | ||||
|             preference { | ||||
|                 key = "pref_action_edit_categories" | ||||
|                 titleRes = R.string.action_edit_categories | ||||
|  | ||||
|                 val catCount = dbCategories.size | ||||
|   | ||||
| @@ -1,8 +1,13 @@ | ||||
| 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 eu.kanade.tachiyomi.R | ||||
| 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.iconTint | ||||
| import eu.kanade.tachiyomi.util.preference.onClick | ||||
| @@ -12,7 +17,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor | ||||
|  | ||||
| class SettingsMainController : SettingsController() { | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.label_settings | ||||
|  | ||||
|         val tintColor = context.getResourceColor(R.attr.colorAccent) | ||||
| @@ -82,4 +87,29 @@ class SettingsMainController : SettingsController() { | ||||
|     private fun navigateTo(controller: SettingsController) { | ||||
|         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 | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -13,7 +13,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values | ||||
|  | ||||
| class SettingsParentalControlsController : SettingsController() { | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.pref_category_parental_controls | ||||
|  | ||||
|         listPreference { | ||||
|   | ||||
| @@ -19,7 +19,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
|  | ||||
| class SettingsReaderController : SettingsController() { | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.pref_category_reader | ||||
|  | ||||
|         intListPreference { | ||||
|   | ||||
| @@ -14,7 +14,7 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
|  | ||||
| class SettingsSecurityController : SettingsController() { | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { | ||||
|         titleRes = R.string.pref_category_security | ||||
|  | ||||
|         if (BiometricManager.from(context).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) { | ||||
|   | ||||
| @@ -31,7 +31,7 @@ class SettingsTrackingController : | ||||
|  | ||||
|     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 | ||||
|  | ||||
|         switchPreference { | ||||
|   | ||||
| @@ -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" | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,168 @@ | ||||
| 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()) | ||||
|     } | ||||
| } | ||||
| @@ -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 | ||||
|     ) | ||||
| } | ||||
| @@ -0,0 +1,42 @@ | ||||
| package eu.kanade.tachiyomi.ui.setting.settingssearch | ||||
|  | ||||
| import android.view.View | ||||
| import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder | ||||
| import kotlin.reflect.full.createInstance | ||||
| 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 | ||||
|  | ||||
| /** | ||||
|  * 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 | ||||
|     } | ||||
| } | ||||
| @@ -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() | ||||
|     } | ||||
| } | ||||
| @@ -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) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										36
									
								
								app/src/main/res/layout/settings_search_controller.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								app/src/main/res/layout/settings_search_controller.xml
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										45
									
								
								app/src/main/res/layout/settings_search_controller_card.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								app/src/main/res/layout/settings_search_controller_card.xml
									
									
									
									
									
										Normal 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> | ||||
|  | ||||
							
								
								
									
										12
									
								
								app/src/main/res/menu/settings_main.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/src/main/res/menu/settings_main.xml
									
									
									
									
									
										Normal 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> | ||||
| @@ -44,6 +44,7 @@ | ||||
|     <string name="action_sort_latest_chapter">Latest chapter</string> | ||||
|     <string name="action_sort_date_added">Date added</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_select_all">Select all</string> | ||||
|     <string name="action_select_inverse">Select inverse</string> | ||||
| @@ -411,6 +412,7 @@ | ||||
|     <string name="licenses">Open source licenses</string> | ||||
|     <string name="check_for_updates">Check for updates</string> | ||||
|     <string name="updated_version">Updated to v%1$s</string> | ||||
|     <string name="about_resources">Resources</string> | ||||
|  | ||||
|     <!-- ACRA --> | ||||
|     <string name="pref_enable_acra">Send crash reports</string> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user