From 812fcec9f0b8f896e5ed96113b0ba5ebcd957f98 Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 5 Jun 2020 09:50:30 -0400 Subject: [PATCH] Move source preferences to separate controller --- .../extension/ExtensionDetailsController.kt | 109 +++-------- .../extension/SourcePreferencesController.kt | 175 ++++++++++++++++++ .../extension/SourcePreferencesPresenter.kt | 14 ++ .../layout/source_preferences_controller.xml | 5 + app/src/main/res/values/strings.xml | 1 + 5 files changed, 218 insertions(+), 86 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/SourcePreferencesController.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/SourcePreferencesPresenter.kt create mode 100644 app/src/main/res/layout/source_preferences_controller.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionDetailsController.kt index d168b9e3ca..177843df34 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionDetailsController.kt @@ -11,13 +11,6 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.appcompat.view.ContextThemeWrapper -import androidx.preference.DialogPreference -import androidx.preference.EditTextPreference -import androidx.preference.EditTextPreferenceDialogController -import androidx.preference.ListPreference -import androidx.preference.ListPreferenceDialogController -import androidx.preference.MultiSelectListPreference -import androidx.preference.MultiSelectListPreferenceDialogController import androidx.preference.Preference import androidx.preference.PreferenceGroupAdapter import androidx.preference.PreferenceManager @@ -28,7 +21,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.EmptyPreferenceDataStore import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore import eu.kanade.tachiyomi.databinding.ExtensionDetailControllerBinding import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.source.CatalogueSource @@ -36,10 +28,13 @@ import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import eu.kanade.tachiyomi.util.preference.checkBoxPreference +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.util.preference.onChange +import eu.kanade.tachiyomi.util.preference.onClick import eu.kanade.tachiyomi.util.preference.preference import eu.kanade.tachiyomi.util.preference.preferenceCategory +import eu.kanade.tachiyomi.util.preference.switchPreference +import eu.kanade.tachiyomi.util.preference.titleRes import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.view.visible import kotlinx.coroutines.flow.launchIn @@ -50,14 +45,11 @@ import uy.kohesive.injekt.injectLazy @SuppressLint("RestrictedApi") class ExtensionDetailsController(bundle: Bundle? = null) : NucleusController(bundle), - NoToolbarElevationController, - PreferenceManager.OnDisplayPreferenceDialogListener, - DialogPreference.TargetFragment { + NoToolbarElevationController { private val preferences: PreferencesHelper by injectLazy() private var preferenceScreen: PreferenceScreen? = null - private var lastOpenPreferencePosition: Int? = null constructor(pkgName: String) : this( Bundle().apply { @@ -114,29 +106,37 @@ class ExtensionDetailsController(bundle: Bundle? = null) : } private fun initPreferences(context: Context, extension: Extension.Installed) { - val themedContext by lazy { getPreferenceThemeContext() } + val themedContext = getPreferenceThemeContext() val manager = PreferenceManager(themedContext) manager.preferenceDataStore = EmptyPreferenceDataStore() - manager.onDisplayPreferenceDialogListener = this val screen = manager.createPreferenceScreen(themedContext) preferenceScreen = screen + val isMultiSource = extension.sources.size > 1 + with(screen) { extension.sources .groupBy { (it as CatalogueSource).lang } .toSortedMap(compareBy { LocaleHelper.getSourceDisplayName(it, context) }) .forEach { preferenceCategory { - title = LocaleHelper.getSourceDisplayName(it.key, context) + if (isMultiSource) { + title = LocaleHelper.getSourceDisplayName(it.key, context) + } + it.value .sortedWith(compareBy({ !it.isEnabled() }, { it.name })) .forEach { source -> val sourcePrefs = mutableListOf() // Source enable/disable - checkBoxPreference { + switchPreference { key = getSourceKey(source.id) - title = source.toString() + title = if (isMultiSource) { + source.toString() + } else { + context.getString(R.string.enabled) + } isPersistent = false isChecked = source.isEnabled() @@ -158,28 +158,11 @@ class ExtensionDetailsController(bundle: Bundle? = null) : // Source preferences if (source is ConfigurableSource) { - // TODO - val dataStore = SharedPreferencesDataStore(/*if (source is HttpSource) { - source.preferences - } else {*/ - context.getSharedPreferences(getSourceKey(source.id), Context.MODE_PRIVATE) - /*}*/ - ) - - val newScreen = screen.preferenceManager.createPreferenceScreen(context) - source.setupPreferenceScreen(newScreen) - - // Reparent the preferences - while (newScreen.preferenceCount != 0) { - val pref = newScreen.getPreference(0) - sourcePrefs.add(pref) - - pref.preferenceDataStore = dataStore - pref.order = Int.MAX_VALUE // reset to default order - pref.isVisible = source.isEnabled() - - newScreen.removePreference(pref) - screen.addPreference(pref) + preference { + titleRes = R.string.label_settings + onClick { + router.pushController(SourcePreferencesController(source.id).withFadeTransaction()) + } } } } @@ -192,16 +175,6 @@ class ExtensionDetailsController(bundle: Bundle? = null) : binding.extensionPrefsRecycler.addItemDecoration(DividerItemDecoration(context, VERTICAL)) } - override fun onSaveInstanceState(outState: Bundle) { - lastOpenPreferencePosition?.let { outState.putInt(LASTOPENPREFERENCE_KEY, it) } - super.onSaveInstanceState(outState) - } - - override fun onRestoreInstanceState(savedInstanceState: Bundle) { - super.onRestoreInstanceState(savedInstanceState) - lastOpenPreferencePosition = savedInstanceState.get(LASTOPENPREFERENCE_KEY) as? Int - } - override fun onDestroyView(view: View) { preferenceScreen = null super.onDestroyView(view) @@ -253,43 +226,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) : return ContextThemeWrapper(activity, tv.resourceId) } - override fun onDisplayPreferenceDialog(preference: Preference) { - if (!isAttached) return - - val screen = preference.parent!! - - lastOpenPreferencePosition = (0 until screen.preferenceCount).indexOfFirst { - screen.getPreference(it) === preference - } - - val f = when (preference) { - is EditTextPreference -> - EditTextPreferenceDialogController - .newInstance(preference.getKey()) - is ListPreference -> - ListPreferenceDialogController - .newInstance(preference.getKey()) - is MultiSelectListPreference -> - MultiSelectListPreferenceDialogController - .newInstance(preference.getKey()) - else -> throw IllegalArgumentException( - "Tried to display dialog for unknown " + - "preference type. Did you forget to override onDisplayPreferenceDialog()?" - ) - } - f.targetController = this - f.showDialog(router) - } - - @Suppress("UNCHECKED_CAST") - override fun findPreference(key: CharSequence): T? { - // We track [lastOpenPreferencePosition] when displaying the dialog - // [key] isn't useful since there may be duplicates - return preferenceScreen!!.getPreference(lastOpenPreferencePosition!!) as T - } - private companion object { const val PKGNAME_KEY = "pkg_name" - const val LASTOPENPREFERENCE_KEY = "last_open_preference" } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/SourcePreferencesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/SourcePreferencesController.kt new file mode 100644 index 0000000000..3cca2f0064 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/SourcePreferencesController.kt @@ -0,0 +1,175 @@ +package eu.kanade.tachiyomi.ui.browse.extension + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Bundle +import android.util.TypedValue +import android.view.ContextThemeWrapper +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.preference.DialogPreference +import androidx.preference.EditTextPreference +import androidx.preference.EditTextPreferenceDialogController +import androidx.preference.ListPreference +import androidx.preference.ListPreferenceDialogController +import androidx.preference.MultiSelectListPreference +import androidx.preference.MultiSelectListPreferenceDialogController +import androidx.preference.Preference +import androidx.preference.PreferenceGroupAdapter +import androidx.preference.PreferenceManager +import androidx.preference.PreferenceScreen +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.DividerItemDecoration.VERTICAL +import androidx.recyclerview.widget.LinearLayoutManager +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.preference.EmptyPreferenceDataStore +import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore +import eu.kanade.tachiyomi.databinding.SourcePreferencesControllerBinding +import eu.kanade.tachiyomi.source.ConfigurableSource +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import timber.log.Timber + +@SuppressLint("RestrictedApi") +class SourcePreferencesController(bundle: Bundle? = null) : + NucleusController(bundle), + PreferenceManager.OnDisplayPreferenceDialogListener, + DialogPreference.TargetFragment { + + private var lastOpenPreferencePosition: Int? = null + + private var preferenceScreen: PreferenceScreen? = null + + constructor(sourceId: Long) : this( + Bundle().apply { + putLong(SOURCE_ID, sourceId) + } + ) + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + val themedInflater = inflater.cloneInContext(getPreferenceThemeContext()) + binding = SourcePreferencesControllerBinding.inflate(themedInflater) + return binding.root + } + + override fun createPresenter(): SourcePreferencesPresenter { + return SourcePreferencesPresenter(args.getLong(SOURCE_ID)) + } + + override fun getTitle(): String? { + return presenter.source?.toString() + } + + @SuppressLint("PrivateResource") + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + val source = presenter.source ?: return + val context = view.context + + val themedContext by lazy { getPreferenceThemeContext() } + val manager = PreferenceManager(themedContext) + manager.preferenceDataStore = EmptyPreferenceDataStore() + manager.onDisplayPreferenceDialogListener = this + val screen = manager.createPreferenceScreen(themedContext) + preferenceScreen = screen + + try { + addPreferencesForSource(screen, source) + } catch (e: AbstractMethodError) { + Timber.e("Source did not implement [addPreferencesForSource]: ${source.name}") + } + + manager.setPreferences(screen) + + binding.recycler.layoutManager = LinearLayoutManager(context) + binding.recycler.adapter = PreferenceGroupAdapter(screen) + binding.recycler.addItemDecoration(DividerItemDecoration(context, VERTICAL)) + } + + override fun onDestroyView(view: View) { + preferenceScreen = null + super.onDestroyView(view) + } + + override fun onSaveInstanceState(outState: Bundle) { + lastOpenPreferencePosition?.let { outState.putInt(LASTOPENPREFERENCE_KEY, it) } + super.onSaveInstanceState(outState) + } + + override fun onRestoreInstanceState(savedInstanceState: Bundle) { + super.onRestoreInstanceState(savedInstanceState) + lastOpenPreferencePosition = savedInstanceState.get(LASTOPENPREFERENCE_KEY) as? Int + } + + private fun addPreferencesForSource(screen: PreferenceScreen, source: Source) { + val context = screen.context + + val dataStore = SharedPreferencesDataStore( + context.getSharedPreferences("source_${source.id}", Context.MODE_PRIVATE) + ) + + if (source is ConfigurableSource) { + val newScreen = screen.preferenceManager.createPreferenceScreen(context) + source.setupPreferenceScreen(newScreen) + + // Reparent the preferences + while (newScreen.preferenceCount != 0) { + val pref = newScreen.getPreference(0) + pref.isIconSpaceReserved = false + pref.preferenceDataStore = dataStore + pref.order = Int.MAX_VALUE // reset to default order + + newScreen.removePreference(pref) + screen.addPreference(pref) + } + } + } + + private fun getPreferenceThemeContext(): Context { + val tv = TypedValue() + activity!!.theme.resolveAttribute(R.attr.preferenceTheme, tv, true) + return ContextThemeWrapper(activity, tv.resourceId) + } + + override fun onDisplayPreferenceDialog(preference: Preference) { + if (!isAttached) return + + val screen = preference.parent!! + + lastOpenPreferencePosition = (0 until screen.preferenceCount).indexOfFirst { + screen.getPreference(it) === preference + } + + val f = when (preference) { + is EditTextPreference -> + EditTextPreferenceDialogController + .newInstance(preference.getKey()) + is ListPreference -> + ListPreferenceDialogController + .newInstance(preference.getKey()) + is MultiSelectListPreference -> + MultiSelectListPreferenceDialogController + .newInstance(preference.getKey()) + else -> throw IllegalArgumentException( + "Tried to display dialog for unknown " + + "preference type. Did you forget to override onDisplayPreferenceDialog()?" + ) + } + f.targetController = this + f.showDialog(router) + } + + @Suppress("UNCHECKED_CAST") + override fun findPreference(key: CharSequence): T? { + // We track [lastOpenPreferencePosition] when displaying the dialog + // [key] isn't useful since there may be duplicates + return preferenceScreen!!.getPreference(lastOpenPreferencePosition!!) as T + } + + private companion object { + const val SOURCE_ID = "source_id" + const val LASTOPENPREFERENCE_KEY = "last_open_preference" + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/SourcePreferencesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/SourcePreferencesPresenter.kt new file mode 100644 index 0000000000..096f76de0c --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/SourcePreferencesPresenter.kt @@ -0,0 +1,14 @@ +package eu.kanade.tachiyomi.ui.browse.extension + +import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class SourcePreferencesPresenter( + val sourceId: Long, + sourceManager: SourceManager = Injekt.get() +) : BasePresenter() { + + val source = sourceManager.get(sourceId) +} diff --git a/app/src/main/res/layout/source_preferences_controller.xml b/app/src/main/res/layout/source_preferences_controller.xml new file mode 100644 index 0000000000..7bf61a8754 --- /dev/null +++ b/app/src/main/res/layout/source_preferences_controller.xml @@ -0,0 +1,5 @@ + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cc49300f69..20e1b7144b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -224,6 +224,7 @@ This extension is not from the official Tachiyomi extensions list. Version: %1$s Language: %1$s + Enabled Fullscreen