From 9cb45b92e1df415d6a0b0040fc47865dad6e89a0 Mon Sep 17 00:00:00 2001 From: arkon Date: Wed, 13 May 2020 23:12:35 -0400 Subject: [PATCH] Move extension preferences to separate controller --- .../extension/ExtensionDetailsController.kt | 158 +------------- .../ExtensionPreferencesController.kt | 196 ++++++++++++++++++ .../ExtensionPreferencesPresenter.kt | 14 ++ .../layout/extension_detail_controller.xml | 73 +++---- .../extension_preferences_controller.xml | 20 ++ 5 files changed, 271 insertions(+), 190 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionPreferencesController.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionPreferencesPresenter.kt create mode 100644 app/src/main/res/layout/extension_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 bdd3db428..b66c03cdb 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 @@ -1,51 +1,23 @@ 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.ExtensionDetailControllerBinding import eu.kanade.tachiyomi.source.ConfigurableSource -import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import eu.kanade.tachiyomi.util.preference.preferenceCategory +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.view.visible import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import reactivecircus.flowbinding.android.view.clicks -import timber.log.Timber -@SuppressLint("RestrictedApi") class ExtensionDetailsController(bundle: Bundle? = null) : - NucleusController(bundle), - PreferenceManager.OnDisplayPreferenceDialogListener, - DialogPreference.TargetFragment { - - private var lastOpenPreferencePosition: Int? = null - - private var preferenceScreen: PreferenceScreen? = null + NucleusController(bundle) { constructor(pkgName: String) : this( Bundle().apply { @@ -54,8 +26,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) : ) override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - val themedInflater = inflater.cloneInContext(getPreferenceThemeContext()) - binding = ExtensionDetailControllerBinding.inflate(themedInflater) + binding = ExtensionDetailControllerBinding.inflate(inflater) return binding.root } @@ -93,132 +64,25 @@ class ExtensionDetailsController(bundle: Bundle? = null) : binding.extensionWarningBanner.setText(R.string.unofficial_extension_message) } - val themedContext by lazy { getPreferenceThemeContext() } - val manager = PreferenceManager(themedContext) - manager.preferenceDataStore = EmptyPreferenceDataStore() - manager.onDisplayPreferenceDialogListener = this - val screen = manager.createPreferenceScreen(themedContext) - preferenceScreen = screen - - val multiSource = extension.sources.size > 1 - - for (source in extension.sources) { - if (source is ConfigurableSource) { - try { - addPreferencesForSource(screen, source, multiSource) - } catch (e: AbstractMethodError) { - Timber.e("Source did not implement [addPreferencesForSource]: ${source.name}") - } - } + if (presenter.extension?.sources?.find { it is ConfigurableSource } != null) { + binding.extensionPrefs.visible() + binding.extensionPrefs.clicks() + .onEach { openPreferences() } + .launchIn(scope) } - - manager.setPreferences(screen) - - binding.extensionPrefsRecycler.layoutManager = LinearLayoutManager(context) - binding.extensionPrefsRecycler.adapter = PreferenceGroupAdapter(screen) - binding.extensionPrefsRecycler.addItemDecoration(DividerItemDecoration(context, VERTICAL)) - - if (screen.preferenceCount == 0) { - binding.extensionPrefsEmptyView.show(R.string.ext_empty_preferences) - } - } - - override fun onDestroyView(view: View) { - preferenceScreen = null - super.onDestroyView(view) } fun onExtensionUninstalled() { router.popCurrentController() } - 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, multiSource: Boolean) { - val context = screen.context - - // TODO - val dataStore = SharedPreferencesDataStore(/*if (source is HttpSource) { - source.preferences - } else {*/ - context.getSharedPreferences("source_${source.id}", Context.MODE_PRIVATE) - /*}*/ + private fun openPreferences() { + router.pushController( + ExtensionPreferencesController(presenter.extension!!.pkgName).withFadeTransaction() ) - - if (source is ConfigurableSource) { - if (multiSource) { - screen.preferenceCategory { - title = source.toString() - } - } - - 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 PKGNAME_KEY = "pkg_name" - const val LASTOPENPREFERENCE_KEY = "last_open_preference" } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionPreferencesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionPreferencesController.kt new file mode 100644 index 000000000..b0ca474ce --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionPreferencesController.kt @@ -0,0 +1,196 @@ +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.ExtensionPreferencesControllerBinding +import eu.kanade.tachiyomi.source.ConfigurableSource +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.util.preference.preferenceCategory +import timber.log.Timber + +@SuppressLint("RestrictedApi") +class ExtensionPreferencesController(bundle: Bundle? = null) : + NucleusController(bundle), + PreferenceManager.OnDisplayPreferenceDialogListener, + DialogPreference.TargetFragment { + + private var lastOpenPreferencePosition: Int? = null + + private var preferenceScreen: PreferenceScreen? = null + + constructor(pkgName: String) : this( + Bundle().apply { + putString(PKGNAME_KEY, pkgName) + } + ) + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + val themedInflater = inflater.cloneInContext(getPreferenceThemeContext()) + binding = ExtensionPreferencesControllerBinding.inflate(themedInflater) + return binding.root + } + + override fun createPresenter(): ExtensionPreferencesPresenter { + return ExtensionPreferencesPresenter(args.getString(PKGNAME_KEY)!!) + } + + override fun getTitle(): String? { + return resources?.getString(R.string.label_extension_info) + } + + @SuppressLint("PrivateResource") + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + val extension = presenter.extension ?: 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 + + val multiSource = extension.sources.size > 1 + + for (source in extension.sources) { + if (source is ConfigurableSource) { + try { + addPreferencesForSource(screen, source, multiSource) + } catch (e: AbstractMethodError) { + Timber.e("Source did not implement [addPreferencesForSource]: ${source.name}") + } + } + } + + manager.setPreferences(screen) + + binding.extensionPrefsRecycler.layoutManager = LinearLayoutManager(context) + binding.extensionPrefsRecycler.adapter = PreferenceGroupAdapter(screen) + binding.extensionPrefsRecycler.addItemDecoration(DividerItemDecoration(context, VERTICAL)) + + if (screen.preferenceCount == 0) { + binding.extensionPrefsEmptyView.show(R.string.ext_empty_preferences) + } + } + + 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, multiSource: Boolean) { + val context = screen.context + + // TODO + val dataStore = SharedPreferencesDataStore(/*if (source is HttpSource) { + source.preferences + } else {*/ + context.getSharedPreferences("source_${source.id}", Context.MODE_PRIVATE) + /*}*/ + ) + + if (source is ConfigurableSource) { + if (multiSource) { + screen.preferenceCategory { + title = source.toString() + } + } + + 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 PKGNAME_KEY = "pkg_name" + const val LASTOPENPREFERENCE_KEY = "last_open_preference" + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionPreferencesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionPreferencesPresenter.kt new file mode 100644 index 000000000..633c05ff4 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionPreferencesPresenter.kt @@ -0,0 +1,14 @@ +package eu.kanade.tachiyomi.ui.browse.extension + +import eu.kanade.tachiyomi.extension.ExtensionManager +import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class ExtensionPreferencesPresenter( + val pkgName: String, + extensionManager: ExtensionManager = Injekt.get() +) : BasePresenter() { + + val extension = extensionManager.installedExtensions.find { it.pkgName == pkgName } +} diff --git a/app/src/main/res/layout/extension_detail_controller.xml b/app/src/main/res/layout/extension_detail_controller.xml index 743f17257..88350a246 100644 --- a/app/src/main/res/layout/extension_detail_controller.xml +++ b/app/src/main/res/layout/extension_detail_controller.xml @@ -5,6 +5,21 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + app:layout_constraintTop_toBottomOf="@id/extension_warning_banner" /> - -