From c838e57f22c596f9679e944c5ae0ef4f54091394 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Fri, 24 Nov 2023 16:53:44 +0200 Subject: [PATCH] Downsample offscreen pages option --- app/build.gradle | 2 +- .../kotatsu/core/prefs/AppSettings.kt | 4 +++ .../RecyclerViewLifecycleDispatcher.kt | 31 +++++++++++++++++++ .../reader/ui/config/ReaderSettings.kt | 6 +++- .../kotatsu/reader/ui/pager/BasePageHolder.kt | 7 +++++ .../reader/ui/pager/PageHolderDelegate.kt | 4 ++- .../reader/ui/pager/standard/PageHolder.kt | 14 +++++++-- .../reader/ui/pager/webtoon/WebtoonHolder.kt | 14 +++++++-- .../ui/pager/webtoon/WebtoonReaderFragment.kt | 2 ++ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/pref_reader.xml | 6 ++++ 11 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/core/ui/list/lifecycle/RecyclerViewLifecycleDispatcher.kt diff --git a/app/build.gradle b/app/build.gradle index 927147f4d..f5d16c7ac 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -134,7 +134,7 @@ dependencies { implementation 'io.coil-kt:coil-base:2.5.0' implementation 'io.coil-kt:coil-svg:2.5.0' - implementation 'com.github.KotatsuApp:subsampling-scale-image-view:2bcc9791b1' + implementation 'com.github.KotatsuApp:subsampling-scale-image-view:0fef1a47c9' implementation 'com.github.solkin:disk-lru-cache:1.4' implementation 'io.noties.markwon:core:4.6.2' diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt index cf527c6df..29d52c439 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -109,6 +109,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { val isReaderTapsAdaptive: Boolean get() = !prefs.getBoolean(KEY_READER_TAPS_LTR, false) + val isReaderOptimizationEnabled: Boolean + get() = prefs.getBoolean(KEY_READER_OPTIMIZE, false) + var isTrafficWarningEnabled: Boolean get() = prefs.getBoolean(KEY_TRAFFIC_WARNING, true) set(value) = prefs.edit { putBoolean(KEY_TRAFFIC_WARNING, value) } @@ -506,6 +509,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { const val KEY_READER_SCREEN_ON = "reader_screen_on" const val KEY_SHORTCUTS = "dynamic_shortcuts" const val KEY_READER_TAPS_LTR = "reader_taps_ltr" + const val KEY_READER_OPTIMIZE = "reader_optimize" const val KEY_LOCAL_LIST_ORDER = "local_order" const val KEY_HISTORY_ORDER = "history_order" const val KEY_WEBTOON_ZOOM = "webtoon_zoom" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/list/lifecycle/RecyclerViewLifecycleDispatcher.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/list/lifecycle/RecyclerViewLifecycleDispatcher.kt new file mode 100644 index 000000000..bf8d940ff --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/list/lifecycle/RecyclerViewLifecycleDispatcher.kt @@ -0,0 +1,31 @@ +package org.koitharu.kotatsu.core.ui.list.lifecycle + +import androidx.core.view.children +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.NO_POSITION + +class RecyclerViewLifecycleDispatcher : RecyclerView.OnScrollListener() { + + private var prevFirst = NO_POSITION + private var prevLast = NO_POSITION + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + val lm = recyclerView.layoutManager as? LinearLayoutManager ?: return + val first = lm.findFirstVisibleItemPosition() + val last = lm.findLastVisibleItemPosition() + if (first == prevFirst && last == prevLast) { + return + } + prevFirst = first + prevLast = last + if (first == NO_POSITION || last == NO_POSITION) { + return + } + for (child in recyclerView.children) { + val wh = recyclerView.getChildViewHolder(child) ?: continue + (wh as? LifecycleAwareViewHolder)?.setIsCurrent(wh.absoluteAdapterPosition in first..last) + } + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt index 86bdb3cf4..96c45e63b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt @@ -36,6 +36,9 @@ class ReaderSettings( val colorFilter: ReaderColorFilter? get() = colorFilterFlow.value?.takeUnless { it.isEmpty } + val isReaderOptimizationEnabled: Boolean + get() = settings.isReaderOptimizationEnabled + val bitmapConfig: Bitmap.Config get() = if (settings.is32BitColorsEnabled) { Bitmap.Config.ARGB_8888 @@ -104,7 +107,8 @@ class ReaderSettings( key == AppSettings.KEY_ZOOM_MODE || key == AppSettings.KEY_PAGES_NUMBERS || key == AppSettings.KEY_READER_BACKGROUND || - key == AppSettings.KEY_32BIT_COLOR + key == AppSettings.KEY_32BIT_COLOR || + key == AppSettings.KEY_READER_OPTIMIZE ) { notifyChanged() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt index 5059e5512..d7e208bda 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/BasePageHolder.kt @@ -7,6 +7,7 @@ import androidx.viewbinding.ViewBinding import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.os.NetworkState import org.koitharu.kotatsu.core.ui.list.lifecycle.LifecycleAwareViewHolder +import org.koitharu.kotatsu.core.util.ext.isLowRamDevice import org.koitharu.kotatsu.databinding.LayoutPageInfoBinding import org.koitharu.kotatsu.reader.domain.PageLoader import org.koitharu.kotatsu.reader.ui.config.ReaderSettings @@ -67,4 +68,10 @@ abstract class BasePageHolder( open fun onRecycled() { delegate.onRecycle() } + + protected fun getBackgroundDownsampling() = when { + !settings.isReaderOptimizationEnabled -> 1 + context.isLowRamDevice() -> 8 + else -> 4 + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt index 80af91780..5f095dbcb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/PageHolderDelegate.kt @@ -42,7 +42,9 @@ class PageHolderDelegate( private var error: Throwable? = null init { - callback.onConfigChanged() + scope.launch(Dispatchers.Main) { // the same as post() -- wait until child fields init + callback.onConfigChanged() + } } fun isLoading() = job?.isActive == true diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt index 42cdffb00..29ffe1bea 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/standard/PageHolder.kt @@ -45,12 +45,22 @@ open class PageHolder( binding.textViewNumber.isVisible = settings.isPagesNumbersEnabled } + override fun onResume() { + super.onResume() + binding.ssiv.downsampling = 1 + } + + override fun onPause() { + super.onPause() + binding.ssiv.downsampling = getBackgroundDownsampling() + } + override fun onConfigChanged() { super.onConfigChanged() - @Suppress("SENSELESS_COMPARISON") - if (settings.applyBitmapConfig(binding.ssiv) && delegate != null) { + if (settings.applyBitmapConfig(binding.ssiv)) { delegate.reload() } + binding.ssiv.downsampling = if (isResumed()) 1 else getBackgroundDownsampling() } @SuppressLint("SetTextI18n") diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt index 764bd2125..e02ce2100 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonHolder.kt @@ -40,10 +40,20 @@ class WebtoonHolder( override fun onConfigChanged() { super.onConfigChanged() - @Suppress("SENSELESS_COMPARISON") - if (settings.applyBitmapConfig(binding.ssiv) && delegate != null) { + if (settings.applyBitmapConfig(binding.ssiv)) { delegate.reload() } + binding.ssiv.downsampling = if (isResumed()) 1 else getBackgroundDownsampling() + } + + override fun onResume() { + super.onResume() + binding.ssiv.downsampling = 1 + } + + override fun onPause() { + super.onPause() + binding.ssiv.downsampling = getBackgroundDownsampling() } override fun onBind(data: ReaderPage) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt index a36b5335d..75decb499 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/webtoon/WebtoonReaderFragment.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.yield import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.os.NetworkState +import org.koitharu.kotatsu.core.ui.list.lifecycle.RecyclerViewLifecycleDispatcher import org.koitharu.kotatsu.core.util.ext.findCenterViewPosition import org.koitharu.kotatsu.core.util.ext.firstVisibleItemPosition import org.koitharu.kotatsu.core.util.ext.observe @@ -44,6 +45,7 @@ class WebtoonReaderFragment : BaseReaderFragment() setHasFixedSize(true) adapter = readerAdapter addOnPageScrollListener(PageScrollListener()) + addOnScrollListener(RecyclerViewLifecycleDispatcher()) } viewModel.isWebtoonZooEnabled.observe(viewLifecycleOwner) { binding.frame.isZoomEnable = it diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 22c42cda3..0897be3d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -527,4 +527,6 @@ Available: %1$d Disable NSFW sources and hide adult manga from list if possible Paused + Reduce memory consumption (beta) + Reduce offscreen pages quality to use less memory diff --git a/app/src/main/res/xml/pref_reader.xml b/app/src/main/res/xml/pref_reader.xml index 57b460aac..06b4a4931 100644 --- a/app/src/main/res/xml/pref_reader.xml +++ b/app/src/main/res/xml/pref_reader.xml @@ -60,6 +60,12 @@ android:summary="@string/enhanced_colors_summary" android:title="@string/enhanced_colors" /> + +