From 5ce2bc92d6d10b0162a62eb90f31be0dd20656ab Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sun, 12 Feb 2023 09:44:53 +0200 Subject: [PATCH] Store and restore ThemeChooserPreference state --- .../kotatsu/core/prefs/AppSettings.kt | 5 +- .../settings/utils/ThemeChooserPreference.kt | 89 +++++++++++++++++++ .../kotatsu/utils/ext/CollectionExt.kt | 8 +- app/src/main/res/layout/preference_theme.xml | 1 + 4 files changed, 99 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 5d9a4bcd6..1239b9cb5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -17,6 +17,7 @@ import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.shelf.domain.ShelfSection import org.koitharu.kotatsu.utils.ext.connectivityManager +import org.koitharu.kotatsu.utils.ext.filterToSet import org.koitharu.kotatsu.utils.ext.getEnumValue import org.koitharu.kotatsu.utils.ext.observe import org.koitharu.kotatsu.utils.ext.putEnumValue @@ -183,7 +184,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { } var hiddenSources: Set - get() = prefs.getStringSet(KEY_SOURCES_HIDDEN, null) ?: emptySet() + get() = prefs.getStringSet(KEY_SOURCES_HIDDEN, null)?.filterToSet { name -> + remoteSources.any { it.name == name } + }.orEmpty() set(value) = prefs.edit { putStringSet(KEY_SOURCES_HIDDEN, value) } val isSourcesSelected: Boolean diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/utils/ThemeChooserPreference.kt b/app/src/main/java/org/koitharu/kotatsu/settings/utils/ThemeChooserPreference.kt index bd417fe36..2775014b0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/utils/ThemeChooserPreference.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/utils/ThemeChooserPreference.kt @@ -3,12 +3,17 @@ package org.koitharu.kotatsu.settings.utils import android.content.Context import android.content.res.TypedArray import android.os.Build +import android.os.Parcel +import android.os.Parcelable import android.util.AttributeSet import android.view.LayoutInflater import android.view.View +import android.view.ViewTreeObserver +import android.widget.HorizontalScrollView import android.widget.LinearLayout import androidx.appcompat.view.ContextThemeWrapper import androidx.core.view.isVisible +import androidx.customview.view.AbsSavedState import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import org.koitharu.kotatsu.R @@ -24,10 +29,12 @@ class ThemeChooserPreference @JvmOverloads constructor( private val entries = ColorScheme.getAvailableList() private var currentValue: ColorScheme = ColorScheme.default + private val lastScrollPosition = intArrayOf(-1) private val itemClickListener = View.OnClickListener { val tag = it.tag as? ColorScheme ?: return@OnClickListener setValueInternal(tag.name, true) } + private var scrollPersistListener: ScrollPersistListener? = null var value: String get() = currentValue.name @@ -36,7 +43,9 @@ class ThemeChooserPreference @JvmOverloads constructor( override fun onBindViewHolder(holder: PreferenceViewHolder) { super.onBindViewHolder(holder) val layout = holder.findViewById(R.id.linear) as? LinearLayout ?: return + val scrollView = holder.findViewById(R.id.scrollView) as? HorizontalScrollView ?: return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + scrollView.suppressLayout(true) layout.suppressLayout(true) } layout.removeAllViews() @@ -52,8 +61,19 @@ class ThemeChooserPreference @JvmOverloads constructor( item.card.setOnClickListener(itemClickListener) layout.addView(item.root) } + if (lastScrollPosition[0] >= 0) { + val scroller = Scroller(scrollView, lastScrollPosition[0]) + scroller.run() + scrollView.post(scroller) + } + scrollView.viewTreeObserver.run { + scrollPersistListener?.let { removeOnScrollChangedListener(it) } + scrollPersistListener = ScrollPersistListener(scrollView, lastScrollPosition) + addOnScrollChangedListener(scrollPersistListener) + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { layout.suppressLayout(false) + scrollView.suppressLayout(false) } } @@ -71,6 +91,24 @@ class ThemeChooserPreference @JvmOverloads constructor( return a.getInt(index, 0) } + override fun onSaveInstanceState(): Parcelable? { + val superState = super.onSaveInstanceState() ?: return null + return SavedState( + superState = superState, + scrollPosition = lastScrollPosition[0], + ) + } + + override fun onRestoreInstanceState(state: Parcelable?) { + if (state !is SavedState) { + super.onRestoreInstanceState(state) + return + } + super.onRestoreInstanceState(state.superState) + lastScrollPosition[0] = state.scrollPosition + // notifyChanged() + } + private fun setValueInternal(enumName: String, notifyChanged: Boolean) { val newValue = ColorScheme.safeValueOf(enumName) ?: return if (newValue != currentValue) { @@ -81,4 +119,55 @@ class ThemeChooserPreference @JvmOverloads constructor( } } } + + private class SavedState : AbsSavedState { + + val scrollPosition: Int + + constructor( + superState: Parcelable, + scrollPosition: Int + ) : super(superState) { + this.scrollPosition = scrollPosition + } + + constructor(source: Parcel, classLoader: ClassLoader?) : super(source, classLoader) { + scrollPosition = source.readInt() + } + + override fun writeToParcel(out: Parcel, flags: Int) { + super.writeToParcel(out, flags) + out.writeInt(scrollPosition) + } + + companion object { + @Suppress("unused") + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(`in`: Parcel) = SavedState(`in`, SavedState::class.java.classLoader) + + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + } + } + + private class ScrollPersistListener( + private val scrollView: HorizontalScrollView, + private val lastScrollPosition: IntArray, + ) : ViewTreeObserver.OnScrollChangedListener { + + override fun onScrollChanged() { + lastScrollPosition[0] = scrollView.scrollX + } + } + + private class Scroller( + private val scrollView: HorizontalScrollView, + private val position: Int, + ) : Runnable { + + override fun run() { + scrollView.scrollTo(position, 0) + } + } } diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CollectionExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CollectionExt.kt index 6f70d2175..10b5fdb5c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CollectionExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CollectionExt.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.utils.ext import androidx.collection.ArraySet -import java.util.* +import java.util.Collections fun MutableList.move(sourceIndex: Int, targetIndex: Int) { if (sourceIndex <= targetIndex) { @@ -11,14 +11,12 @@ fun MutableList.move(sourceIndex: Int, targetIndex: Int) { } } -@Suppress("FunctionName") inline fun MutableSet(size: Int, init: (index: Int) -> T): MutableSet { val set = ArraySet(size) repeat(size) { index -> set.add(init(index)) } return set } -@Suppress("FunctionName") inline fun Set(size: Int, init: (index: Int) -> T): Set = when (size) { 0 -> emptySet() 1 -> Collections.singleton(init(0)) @@ -39,3 +37,7 @@ fun Map.findKeyByValue(value: V): K? { } return null } + +inline fun Collection.filterToSet(predicate: (T) -> Boolean): Set { + return filterTo(ArraySet(size), predicate) +} diff --git a/app/src/main/res/layout/preference_theme.xml b/app/src/main/res/layout/preference_theme.xml index 68b925f8c..ef3586ff9 100644 --- a/app/src/main/res/layout/preference_theme.xml +++ b/app/src/main/res/layout/preference_theme.xml @@ -55,6 +55,7 @@