From 965af17a2a13d637426a7359bd8cdc4de1548581 Mon Sep 17 00:00:00 2001 From: Jays2Kings Date: Wed, 31 Mar 2021 16:28:53 -0400 Subject: [PATCH] Fix browse scrolling back to top same with extensions + migrations --- .../data/preference/PreferencesHelper.kt | 2 +- .../ui/extension/ExtensionBottomSheet.kt | 128 +++++++++++------ .../ui/extension/RecyclerViewPagerAdapter.kt | 34 +++++ .../ui/extension/RecyclerWithScrollerView.kt | 25 ++++ .../tachiyomi/ui/recents/RecentsPresenter.kt | 2 +- .../tachiyomi/ui/source/BrowseController.kt | 50 ++++--- .../tachiyomi/ui/source/SourcePresenter.kt | 134 ++++++++++-------- app/src/main/res/layout/browse_controller.xml | 3 +- .../res/layout/recycler_with_scroller.xml | 4 +- 9 files changed, 261 insertions(+), 121 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/extension/RecyclerViewPagerAdapter.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/extension/RecyclerWithScrollerView.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 7dc7797696..5765e29b95 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -151,7 +151,7 @@ class PreferencesHelper(val context: Context) { fun autoUpdateTrack() = prefs.getBoolean(Keys.autoUpdateTrack, true) - fun lastUsedCatalogueSource() = rxPrefs.getLong(Keys.lastUsedCatalogueSource, -1) + fun lastUsedCatalogueSource() = flowPrefs.getLong(Keys.lastUsedCatalogueSource, -1) fun lastUsedCategory() = rxPrefs.getInteger(Keys.lastUsedCategory, 0) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt index db9050b329..89c5372326 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionBottomSheet.kt @@ -6,6 +6,9 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout +import androidx.core.view.get +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager.widget.PagerAdapter.POSITION_NONE import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.tabs.TabLayout import eu.davidea.flexibleadapter.FlexibleAdapter @@ -28,9 +31,7 @@ import eu.kanade.tachiyomi.util.view.collapse import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets import eu.kanade.tachiyomi.util.view.expand import eu.kanade.tachiyomi.util.view.isExpanded -import eu.kanade.tachiyomi.util.view.updatePaddingRelative import eu.kanade.tachiyomi.util.view.withFadeTransaction -import eu.kanade.tachiyomi.widget.ViewPagerAdapter import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -49,9 +50,12 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At /** * Adapter containing the list of extensions */ - private var adapter: FlexibleAdapter>? = null + private var adapter: ExtensionAdapter? = null private var migAdapter: FlexibleAdapter>? = null + val adapters + get() = listOf(adapter, migAdapter) + val presenter = ExtensionBottomPresenter(this) private var extensions: List = emptyList() @@ -59,9 +63,12 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At private lateinit var binding: ExtensionsBottomSheetBinding lateinit var controller: BrowseController + var boundViews = arrayListOf() - val extensionFrameLayout = RecyclerWithScrollerBinding.inflate(LayoutInflater.from(context)) - val migrationFrameLayout = RecyclerWithScrollerBinding.inflate(LayoutInflater.from(context)) + val extensionFrameLayout: RecyclerWithScrollerView? + get() = binding.pager.findViewWithTag("TabbedRecycler0") as? RecyclerWithScrollerView + val migrationFrameLayout: RecyclerWithScrollerView? + get() = binding.pager.findViewWithTag("TabbedRecycler1") as? RecyclerWithScrollerView override fun onFinishInflate() { super.onFinishInflate() @@ -71,29 +78,22 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At fun onCreate(controller: BrowseController) { // Initialize adapter, scroll listener and recycler views adapter = ExtensionAdapter(this) - migAdapter = ExtensionAdapter(this) + adapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY + if (migAdapter == null) { + migAdapter = SourceAdapter(this) + } + migAdapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY sheetBehavior = BottomSheetBehavior.from(this) // Create recycler and set adapter. - val extRecyler = extensionFrameLayout.recycler - val migRecyler = migrationFrameLayout.recycler - extRecyler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context) - extRecyler.adapter = adapter - extRecyler.setHasFixedSize(true) - extRecyler.addItemDecoration(ExtensionDividerItemDecoration(context)) - - migRecyler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context) - migRecyler.setHasFixedSize(true) - - adapter?.fastScroller = extensionFrameLayout.fastScroller + binding.pager.adapter = TabbedSheetAdapter() + binding.tabs.setupWithViewPager(binding.pager) this.controller = controller binding.pager.doOnApplyWindowInsets { _, _, _ -> val bottomBar = controller.activityBinding?.bottomNav - extRecyler.updatePaddingRelative(bottom = bottomBar?.height ?: 0) - migRecyler.updatePaddingRelative(bottom = bottomBar?.height ?: 0) + // extRecyler?.updatePaddingRelative(bottom = bottomBar?.height ?: 0) + // migRecyler?.updatePaddingRelative(bottom = bottomBar?.height ?: 0) } - binding.pager.adapter = TabbedSheetAdapter() - binding.tabs.setupWithViewPager(binding.pager) binding.tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab?) { if (canExpand) { @@ -103,18 +103,18 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At when (tab?.position) { 0 -> extensionFrameLayout else -> migrationFrameLayout - }.recycler.isNestedScrollingEnabled = true + }?.binding?.recycler?.isNestedScrollingEnabled = true when (tab?.position) { 0 -> extensionFrameLayout else -> migrationFrameLayout - }.recycler.requestLayout() + }?.binding?.recycler?.requestLayout() } override fun onTabUnselected(tab: TabLayout.Tab?) { when (tab?.position) { 0 -> extensionFrameLayout else -> migrationFrameLayout - }.recycler.isNestedScrollingEnabled = false + }?.binding?.recycler?.isNestedScrollingEnabled = false if (tab?.position == 1) { presenter.deselectSource() } @@ -125,7 +125,7 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At when (tab?.position) { 0 -> extensionFrameLayout else -> migrationFrameLayout - }.recycler.isNestedScrollingEnabled = true + }?.binding?.recycler?.isNestedScrollingEnabled = true } }) presenter.onCreate() @@ -144,7 +144,7 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At fun updatedNestedRecyclers() { listOf(extensionFrameLayout, migrationFrameLayout).forEachIndexed { index, recyclerWithScrollerBinding -> - recyclerWithScrollerBinding.recycler.isNestedScrollingEnabled = binding.pager.currentItem == index + recyclerWithScrollerBinding?.binding?.recycler?.isNestedScrollingEnabled = binding.pager.currentItem == index } } @@ -162,7 +162,7 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At } override fun onButtonClick(position: Int) { - val extension = (adapter?.getItem(position) as? ExtensionItem)?.extension ?: return + val extension = (migAdapter?.getItem(position) as? ExtensionItem)?.extension ?: return when (extension) { is Extension.Installed -> { if (!extension.hasUpdate) { @@ -249,21 +249,19 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At } fun setMigrationSources(sources: List) { - val migRecyler = migrationFrameLayout.recycler if (migAdapter !is SourceAdapter) { migAdapter = SourceAdapter(this) - migRecyler.adapter = migAdapter - migAdapter?.fastScroller = migrationFrameLayout.fastScroller + migrationFrameLayout?.onBind(migAdapter!!) + migAdapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY } migAdapter?.updateDataSet(sources, true) } fun setMigrationManga(manga: List?) { - val migRecyler = migrationFrameLayout.recycler if (migAdapter !is MangaAdapter) { migAdapter = MangaAdapter(this) - migRecyler.adapter = migAdapter - migAdapter?.fastScroller = migrationFrameLayout.fastScroller + migrationFrameLayout?.onBind(migAdapter!!) + migAdapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY } migAdapter?.updateDataSet(manga, true) } @@ -301,14 +299,7 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At presenter.uninstallExtension(pkgName) } - private inner class TabbedSheetAdapter : ViewPagerAdapter() { - - override fun createView(container: ViewGroup, position: Int): View { - return when (position) { - 0 -> extensionFrameLayout.root - else -> migrationFrameLayout.root - } - } + private inner class TabbedSheetAdapter : RecyclerViewPagerAdapter() { override fun getCount(): Int { return 2 @@ -322,5 +313,60 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At } ) } + + /** + * Creates a new view for this adapter. + * + * @return a new view. + */ + override fun createView(container: ViewGroup): View { + val binding = RecyclerWithScrollerBinding.inflate(LayoutInflater.from(container.context), container, false) + val view: RecyclerWithScrollerView = binding.root + view.setUp(this@ExtensionBottomSheet, binding) + return view + } + + /** + * Binds a view with a position. + * + * @param view the view to bind. + * @param position the position in the adapter. + */ + override fun bindView(view: View, position: Int) { + (view as RecyclerWithScrollerView).onBind(adapters[position]!!) + view.setTag("TabbedRecycler$position") + boundViews.add(view) + } + + /** + * Recycles a view. + * + * @param view the view to recycle. + * @param position the position in the adapter. + */ + override fun recycleView(view: View, position: Int) { + // (view as RecyclerWithScrollerView).onRecycle() + boundViews.remove(view) + } + + /** + * Returns the position of the view. + */ + override fun getItemPosition(obj: Any): Int { + val view = (obj as? RecyclerWithScrollerView) ?: return POSITION_NONE + val index = adapters.indexOfFirst { it == view.binding?.recycler?.adapter } + return if (index == -1) POSITION_NONE else index + } + + /** + * Called when the view of this adapter is being destroyed. + */ + fun onDestroy() { + /*for (view in boundViews) { + if (view is LibraryCategoryView) { + view.onDestroy() + } + }*/ + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/RecyclerViewPagerAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/RecyclerViewPagerAdapter.kt new file mode 100644 index 0000000000..f446b838af --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/RecyclerViewPagerAdapter.kt @@ -0,0 +1,34 @@ +package eu.kanade.tachiyomi.ui.extension + +import android.view.View +import android.view.ViewGroup +import com.nightlynexus.viewstatepageradapter.ViewStatePagerAdapter +import java.util.Stack + +abstract class RecyclerViewPagerAdapter : ViewStatePagerAdapter() { + + private val pool = Stack() + + var recycle = true + set(value) { + if (!value) pool.clear() + field = value + } + + protected abstract fun createView(container: ViewGroup): View + + protected abstract fun bindView(view: View, position: Int) + + protected open fun recycleView(view: View, position: Int) {} + + override fun createView(container: ViewGroup, position: Int): View { + val view = if (pool.isNotEmpty()) pool.pop() else createView(container) + bindView(view, position) + return view + } + + override fun destroyView(container: ViewGroup, position: Int, view: View) { + recycleView(view, position) + if (recycle) pool.push(view) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/RecyclerWithScrollerView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/RecyclerWithScrollerView.kt new file mode 100644 index 0000000000..54de439a7e --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/RecyclerWithScrollerView.kt @@ -0,0 +1,25 @@ +package eu.kanade.tachiyomi.ui.extension + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.databinding.RecyclerWithScrollerBinding + +class RecyclerWithScrollerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + FrameLayout(context, attrs) { + + var binding: RecyclerWithScrollerBinding? = null + fun setUp(sheet: ExtensionBottomSheet, binding: RecyclerWithScrollerBinding) { + binding.recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context) + binding.recycler.setHasFixedSize(true) + binding.recycler.addItemDecoration(ExtensionDividerItemDecoration(context)) + this.binding = binding + } + + fun onBind(adapter: FlexibleAdapter>) { + binding?.recycler?.adapter = adapter + adapter.fastScroller = binding?.fastScroller + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt index 4a498f3f3b..beabdc1dab 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt @@ -367,7 +367,7 @@ class RecentsPresenter( getRecents() } - companion object { + private companion object { var lastRecents: List? = null } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt index 1f1fee573e..99ed22b885 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt @@ -13,6 +13,7 @@ import android.view.ViewGroup import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils +import androidx.recyclerview.widget.RecyclerView import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.RouterTransaction @@ -27,7 +28,7 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.databinding.BrowseControllerBinding import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.Source -import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.extension.SettingsExtensionsController import eu.kanade.tachiyomi.ui.main.BottomSheetController import eu.kanade.tachiyomi.ui.main.MainActivity @@ -66,7 +67,7 @@ import kotlin.math.min * [SourceAdapter.OnLatestClickListener] call function data on latest item click */ class BrowseController : - NucleusController(), + BaseController(), FlexibleAdapter.OnItemClickListener, SourceAdapter.SourceListener, RootSearchInterface, @@ -109,9 +110,7 @@ class BrowseController : } else view?.context?.getString(R.string.browse) } - override fun createPresenter(): SourcePresenter { - return SourcePresenter() - } + val presenter = SourcePresenter(this) override fun createBinding(inflater: LayoutInflater) = BrowseControllerBinding.inflate(inflater) @@ -119,29 +118,34 @@ class BrowseController : super.onViewCreated(view) adapter = SourceAdapter(this) + // Create binding.sourceRecycler and set adapter. + binding.sourceRecycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) - // Create binding.recycler and set adapter. - binding.recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) - binding.recycler.adapter = adapter + binding.sourceRecycler.adapter = adapter adapter?.isSwipeEnabled = true - // binding.recycler.addItemDecoration(SourceDividerItemDecoration(view.context)) + adapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY + // binding.sourceRecycler.addItemDecoration(SourceDividerItemDecoration(view.context)) val attrsArray = intArrayOf(android.R.attr.actionBarSize) val array = view.context.obtainStyledAttributes(attrsArray) val appBarHeight = array.getDimensionPixelSize(0, 0) array.recycle() scrollViewWith( - binding.recycler, + binding.sourceRecycler, afterInsets = { headerHeight = it.systemWindowInsetTop + appBarHeight - binding.recycler.updatePaddingRelative(bottom = activityBinding?.bottomNav?.height ?: 0) + binding.sourceRecycler.updatePaddingRelative(bottom = activityBinding?.bottomNav?.height ?: 0) }, onBottomNavUpdate = { setBottomPadding() } ) - binding.recycler.post { + binding.sourceRecycler.post { setBottomSheetTabs(if (binding.bottomSheet.root.sheetBehavior.isCollapsed()) 0f else 1f) + activityBinding?.appBar?.elevation = min( + if (binding.bottomSheet.root.sheetBehavior.isCollapsed()) 0f else 1f * 15f, + if (binding.sourceRecycler.canScrollVertically(-1)) 15f else 0f + ) } requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) @@ -154,7 +158,7 @@ class BrowseController : binding.shadow2.alpha = (1 - max(0f, progress)) * 0.25f activityBinding?.appBar?.elevation = min( (1f - progress) * 15f, - if (binding.recycler.canScrollVertically(-1)) 15f else 0f + if (binding.sourceRecycler.canScrollVertically(-1)) 15f else 0f ) activityBinding?.appBar?.y = max(activityBinding!!.appBar.y, -headerHeight * (1 - progress)) val oldShow = showingExtensions @@ -200,6 +204,10 @@ class BrowseController : if (showingExtensions) { binding.bottomSheet.root.sheetBehavior?.expand() } + presenter.onCreate() + if (presenter.sourceItems.isNotEmpty()) { + setSources(presenter.sourceItems, presenter.lastUsedItem) + } } fun updateTitleAndMenu() { @@ -256,10 +264,10 @@ class BrowseController : ) binding.shadow2.translationY = pad binding.bottomSheet.root.sheetBehavior?.peekHeight = 58.spToPx + padding - binding.bottomSheet.root.extensionFrameLayout.fastScroller.updateLayoutParams { + binding.bottomSheet.root.extensionFrameLayout?.binding?.fastScroller?.updateLayoutParams { bottomMargin = -pad.toInt() } - binding.bottomSheet.root.migrationFrameLayout.fastScroller.updateLayoutParams { + binding.bottomSheet.root.migrationFrameLayout?.binding?.fastScroller?.updateLayoutParams { bottomMargin = -pad.toInt() } } @@ -293,6 +301,11 @@ class BrowseController : super.onDestroyView(view) } + override fun onDestroy() { + super.onDestroy() + presenter.onDestroy() + } + override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { super.onChangeStarted(handler, type) if (!type.isPush) { @@ -305,7 +318,7 @@ class BrowseController : activityBinding?.appBar?.elevation = when { binding.bottomSheet.root.sheetBehavior.isExpanded() -> 0f - binding.recycler.canScrollVertically(-1) -> 15f + binding.sourceRecycler.canScrollVertically(-1) -> 15f else -> 0f } } else { @@ -483,8 +496,9 @@ class BrowseController : /** * Called to update adapter containing sources. */ - fun setSources(sources: List>) { - adapter?.updateDataSet(sources) + fun setSources(sources: List>, lastUsed: SourceItem?) { + adapter?.updateDataSet(sources, false) + setLastUsedSource(lastUsed) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourcePresenter.kt index 2cee43ddd2..1bf94d74d2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourcePresenter.kt @@ -1,19 +1,20 @@ package eu.kanade.tachiyomi.ui.source -import android.os.Bundle import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.SourceManager -import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter -import rx.Observable -import rx.Subscription -import rx.android.schedulers.AndroidSchedulers +import eu.kanade.tachiyomi.util.system.withUIContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.TreeMap -import java.util.concurrent.TimeUnit /** * Presenter of [BrowseController] @@ -23,86 +24,102 @@ import java.util.concurrent.TimeUnit * @param preferences application preferences. */ class SourcePresenter( + val controller: BrowseController, val sourceManager: SourceManager = Injekt.get(), private val preferences: PreferencesHelper = Injekt.get() -) : BasePresenter() { +) { + private var scope = CoroutineScope(Job() + Dispatchers.Default) var sources = getEnabledSources() - /** - * Subscription for retrieving enabled sources. - */ - private var sourceSubscription: Subscription? = null - private var lastUsedSubscription: Subscription? = null + var sourceItems = emptyList() + var lastUsedItem: SourceItem? = null - override fun onCreate(savedState: Bundle?) { - super.onCreate(savedState) + var lastUsedJob: Job? = null + + fun onCreate() { + if (lastSources != null) { + if (sourceItems.isEmpty()) { + sourceItems = lastSources ?: emptyList() + } + lastUsedItem = lastUsedItemRem + lastSources = null + lastUsedItemRem = null + } // Load enabled and last used sources loadSources() - loadLastUsedSource() } /** * Unsubscribe and create a new subscription to fetch enabled sources. */ private fun loadSources() { - sourceSubscription?.unsubscribe() + scope.launch { + val pinnedSources = mutableListOf() + val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault() - val pinnedSources = mutableListOf() - val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault() - - val map = TreeMap> { d1, d2 -> - // Catalogues without a lang defined will be placed at the end - when { - d1 == "" && d2 != "" -> 1 - d2 == "" && d1 != "" -> -1 - else -> d1.compareTo(d2) - } - } - val byLang = sources.groupByTo(map, { it.lang }) - var sourceItems = byLang.flatMap { - val langItem = LangItem(it.key) - it.value.map { source -> - val isPinned = source.id.toString() in pinnedCatalogues - if (source.id.toString() in pinnedCatalogues) { - pinnedSources.add(SourceItem(source, LangItem(PINNED_KEY))) + val map = TreeMap> { d1, d2 -> + // Catalogues without a lang defined will be placed at the end + when { + d1 == "" && d2 != "" -> 1 + d2 == "" && d1 != "" -> -1 + else -> d1.compareTo(d2) } + } + val byLang = sources.groupByTo(map, { it.lang }) + sourceItems = byLang.flatMap { + val langItem = LangItem(it.key) + it.value.map { source -> + val isPinned = source.id.toString() in pinnedCatalogues + if (source.id.toString() in pinnedCatalogues) { + pinnedSources.add(SourceItem(source, LangItem(PINNED_KEY))) + } - SourceItem(source, langItem, isPinned) + SourceItem(source, langItem, isPinned) + } + } + + if (pinnedSources.isNotEmpty()) { + sourceItems = pinnedSources + sourceItems + } + + lastUsedItem = getLastUsedSource(preferences.lastUsedCatalogueSource().get()) + withUIContext { + controller.setSources(sourceItems, lastUsedItem) + loadLastUsedSource() } } - - if (pinnedSources.isNotEmpty()) { - sourceItems = pinnedSources + sourceItems - } - - sourceSubscription = Observable.just(sourceItems) - .subscribeLatestCache(BrowseController::setSources) } private fun loadLastUsedSource() { - lastUsedSubscription?.unsubscribe() - val sharedObs = preferences.lastUsedCatalogueSource().asObservable().share() + lastUsedJob?.cancel() + lastUsedJob = preferences.lastUsedCatalogueSource().asFlow() + .onEach { + lastUsedItem = getLastUsedSource(it) + withUIContext { + controller.setLastUsedSource(lastUsedItem) + } + }.launchIn(scope) + } - // Emit the first item immediately but delay subsequent emissions by 500ms. - lastUsedSubscription = Observable.merge( - sharedObs.take(1), - sharedObs.skip(1).delay(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) - ).distinctUntilChanged().map { - (sourceManager.get(it) as? CatalogueSource)?.let { source -> - val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault() - val isPinned = source.id.toString() in pinnedCatalogues - if (isPinned) null - else SourceItem(source, null, isPinned) - } - }.subscribeLatestCache(BrowseController::setLastUsedSource) + private fun getLastUsedSource(value: Long): SourceItem? { + return (sourceManager.get(value) as? CatalogueSource)?.let { source -> + val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault() + val isPinned = source.id.toString() in pinnedCatalogues + if (isPinned) null + else SourceItem(source, null, isPinned) + } } fun updateSources() { sources = getEnabledSources() loadSources() - loadLastUsedSource() + } + + fun onDestroy() { + lastSources = sourceItems + lastUsedItemRem = lastUsedItem } /** @@ -124,5 +141,8 @@ class SourcePresenter( companion object { const val PINNED_KEY = "pinned" const val LAST_USED_KEY = "last_used" + + private var lastSources: List? = null + private var lastUsedItemRem: SourceItem? = null } } diff --git a/app/src/main/res/layout/browse_controller.xml b/app/src/main/res/layout/browse_controller.xml index 7c92502647..2be0617022 100644 --- a/app/src/main/res/layout/browse_controller.xml +++ b/app/src/main/res/layout/browse_controller.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/browse_layout" xmlns:app="http://schemas.android.com/apk/res-auto"> - - +