mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 20:01:53 +01:00
Fix browse scrolling back to top
same with extensions + migrations
This commit is contained in:
parent
f0fb7b3d49
commit
965af17a2a
@ -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)
|
||||
|
||||
|
@ -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<IFlexible<*>>? = null
|
||||
private var adapter: ExtensionAdapter? = null
|
||||
private var migAdapter: FlexibleAdapter<IFlexible<*>>? = null
|
||||
|
||||
val adapters
|
||||
get() = listOf(adapter, migAdapter)
|
||||
|
||||
val presenter = ExtensionBottomPresenter(this)
|
||||
|
||||
private var extensions: List<ExtensionItem> = 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<RecyclerWithScrollerView>()
|
||||
|
||||
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<SourceItem>) {
|
||||
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<MangaItem>?) {
|
||||
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()
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<View>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
@ -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<IFlexible<*>>) {
|
||||
binding?.recycler?.adapter = adapter
|
||||
adapter.fastScroller = binding?.fastScroller
|
||||
}
|
||||
}
|
@ -367,7 +367,7 @@ class RecentsPresenter(
|
||||
getRecents()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private companion object {
|
||||
var lastRecents: List<RecentMangaItem>? = null
|
||||
}
|
||||
}
|
||||
|
@ -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<BrowseControllerBinding, SourcePresenter>(),
|
||||
BaseController<BrowseControllerBinding>(),
|
||||
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<ViewGroup.MarginLayoutParams> {
|
||||
binding.bottomSheet.root.extensionFrameLayout?.binding?.fastScroller?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = -pad.toInt()
|
||||
}
|
||||
binding.bottomSheet.root.migrationFrameLayout.fastScroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
binding.bottomSheet.root.migrationFrameLayout?.binding?.fastScroller?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
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<IFlexible<*>>) {
|
||||
adapter?.updateDataSet(sources)
|
||||
fun setSources(sources: List<IFlexible<*>>, lastUsed: SourceItem?) {
|
||||
adapter?.updateDataSet(sources, false)
|
||||
setLastUsedSource(lastUsed)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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<BrowseController>() {
|
||||
) {
|
||||
|
||||
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<SourceItem>()
|
||||
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<SourceItem>()
|
||||
val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault()
|
||||
|
||||
val pinnedSources = mutableListOf<SourceItem>()
|
||||
val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault()
|
||||
|
||||
val map = TreeMap<String, MutableList<CatalogueSource>> { 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<String, MutableList<CatalogueSource>> { 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<SourceItem>? = null
|
||||
private var lastUsedItemRem: SourceItem? = null
|
||||
}
|
||||
}
|
||||
|
@ -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">
|
||||
|
||||
<FrameLayout
|
||||
@ -12,7 +13,7 @@
|
||||
android:background="?android:attr/colorBackground">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:id="@+id/source_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<eu.kanade.tachiyomi.ui.extension.RecyclerWithScrollerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/recycler_layout"
|
||||
@ -21,4 +21,4 @@
|
||||
android:layout_height="match_parent"
|
||||
app:fastScrollerBubbleEnabled="false" />
|
||||
|
||||
</FrameLayout>
|
||||
</eu.kanade.tachiyomi.ui.extension.RecyclerWithScrollerView>
|
||||
|
Loading…
Reference in New Issue
Block a user