mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-10 01:35:09 +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 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)
|
fun lastUsedCategory() = rxPrefs.getInteger(Keys.lastUsedCategory, 0)
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
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.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
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.doOnApplyWindowInsets
|
||||||
import eu.kanade.tachiyomi.util.view.expand
|
import eu.kanade.tachiyomi.util.view.expand
|
||||||
import eu.kanade.tachiyomi.util.view.isExpanded
|
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.util.view.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
@ -49,9 +50,12 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
/**
|
/**
|
||||||
* Adapter containing the list of extensions
|
* Adapter containing the list of extensions
|
||||||
*/
|
*/
|
||||||
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
|
private var adapter: ExtensionAdapter? = null
|
||||||
private var migAdapter: FlexibleAdapter<IFlexible<*>>? = null
|
private var migAdapter: FlexibleAdapter<IFlexible<*>>? = null
|
||||||
|
|
||||||
|
val adapters
|
||||||
|
get() = listOf(adapter, migAdapter)
|
||||||
|
|
||||||
val presenter = ExtensionBottomPresenter(this)
|
val presenter = ExtensionBottomPresenter(this)
|
||||||
|
|
||||||
private var extensions: List<ExtensionItem> = emptyList()
|
private var extensions: List<ExtensionItem> = emptyList()
|
||||||
@ -59,9 +63,12 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
private lateinit var binding: ExtensionsBottomSheetBinding
|
private lateinit var binding: ExtensionsBottomSheetBinding
|
||||||
|
|
||||||
lateinit var controller: BrowseController
|
lateinit var controller: BrowseController
|
||||||
|
var boundViews = arrayListOf<RecyclerWithScrollerView>()
|
||||||
|
|
||||||
val extensionFrameLayout = RecyclerWithScrollerBinding.inflate(LayoutInflater.from(context))
|
val extensionFrameLayout: RecyclerWithScrollerView?
|
||||||
val migrationFrameLayout = RecyclerWithScrollerBinding.inflate(LayoutInflater.from(context))
|
get() = binding.pager.findViewWithTag("TabbedRecycler0") as? RecyclerWithScrollerView
|
||||||
|
val migrationFrameLayout: RecyclerWithScrollerView?
|
||||||
|
get() = binding.pager.findViewWithTag("TabbedRecycler1") as? RecyclerWithScrollerView
|
||||||
|
|
||||||
override fun onFinishInflate() {
|
override fun onFinishInflate() {
|
||||||
super.onFinishInflate()
|
super.onFinishInflate()
|
||||||
@ -71,29 +78,22 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
fun onCreate(controller: BrowseController) {
|
fun onCreate(controller: BrowseController) {
|
||||||
// Initialize adapter, scroll listener and recycler views
|
// Initialize adapter, scroll listener and recycler views
|
||||||
adapter = ExtensionAdapter(this)
|
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)
|
sheetBehavior = BottomSheetBehavior.from(this)
|
||||||
// Create recycler and set adapter.
|
// Create recycler and set adapter.
|
||||||
val extRecyler = extensionFrameLayout.recycler
|
|
||||||
val migRecyler = migrationFrameLayout.recycler
|
|
||||||
|
|
||||||
extRecyler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context)
|
binding.pager.adapter = TabbedSheetAdapter()
|
||||||
extRecyler.adapter = adapter
|
binding.tabs.setupWithViewPager(binding.pager)
|
||||||
extRecyler.setHasFixedSize(true)
|
|
||||||
extRecyler.addItemDecoration(ExtensionDividerItemDecoration(context))
|
|
||||||
|
|
||||||
migRecyler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context)
|
|
||||||
migRecyler.setHasFixedSize(true)
|
|
||||||
|
|
||||||
adapter?.fastScroller = extensionFrameLayout.fastScroller
|
|
||||||
this.controller = controller
|
this.controller = controller
|
||||||
binding.pager.doOnApplyWindowInsets { _, _, _ ->
|
binding.pager.doOnApplyWindowInsets { _, _, _ ->
|
||||||
val bottomBar = controller.activityBinding?.bottomNav
|
val bottomBar = controller.activityBinding?.bottomNav
|
||||||
extRecyler.updatePaddingRelative(bottom = bottomBar?.height ?: 0)
|
// extRecyler?.updatePaddingRelative(bottom = bottomBar?.height ?: 0)
|
||||||
migRecyler.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 {
|
binding.tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||||
override fun onTabSelected(tab: TabLayout.Tab?) {
|
override fun onTabSelected(tab: TabLayout.Tab?) {
|
||||||
if (canExpand) {
|
if (canExpand) {
|
||||||
@ -103,18 +103,18 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
when (tab?.position) {
|
when (tab?.position) {
|
||||||
0 -> extensionFrameLayout
|
0 -> extensionFrameLayout
|
||||||
else -> migrationFrameLayout
|
else -> migrationFrameLayout
|
||||||
}.recycler.isNestedScrollingEnabled = true
|
}?.binding?.recycler?.isNestedScrollingEnabled = true
|
||||||
when (tab?.position) {
|
when (tab?.position) {
|
||||||
0 -> extensionFrameLayout
|
0 -> extensionFrameLayout
|
||||||
else -> migrationFrameLayout
|
else -> migrationFrameLayout
|
||||||
}.recycler.requestLayout()
|
}?.binding?.recycler?.requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTabUnselected(tab: TabLayout.Tab?) {
|
override fun onTabUnselected(tab: TabLayout.Tab?) {
|
||||||
when (tab?.position) {
|
when (tab?.position) {
|
||||||
0 -> extensionFrameLayout
|
0 -> extensionFrameLayout
|
||||||
else -> migrationFrameLayout
|
else -> migrationFrameLayout
|
||||||
}.recycler.isNestedScrollingEnabled = false
|
}?.binding?.recycler?.isNestedScrollingEnabled = false
|
||||||
if (tab?.position == 1) {
|
if (tab?.position == 1) {
|
||||||
presenter.deselectSource()
|
presenter.deselectSource()
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
when (tab?.position) {
|
when (tab?.position) {
|
||||||
0 -> extensionFrameLayout
|
0 -> extensionFrameLayout
|
||||||
else -> migrationFrameLayout
|
else -> migrationFrameLayout
|
||||||
}.recycler.isNestedScrollingEnabled = true
|
}?.binding?.recycler?.isNestedScrollingEnabled = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
presenter.onCreate()
|
presenter.onCreate()
|
||||||
@ -144,7 +144,7 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
|
|
||||||
fun updatedNestedRecyclers() {
|
fun updatedNestedRecyclers() {
|
||||||
listOf(extensionFrameLayout, migrationFrameLayout).forEachIndexed { index, recyclerWithScrollerBinding ->
|
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) {
|
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) {
|
when (extension) {
|
||||||
is Extension.Installed -> {
|
is Extension.Installed -> {
|
||||||
if (!extension.hasUpdate) {
|
if (!extension.hasUpdate) {
|
||||||
@ -249,21 +249,19 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setMigrationSources(sources: List<SourceItem>) {
|
fun setMigrationSources(sources: List<SourceItem>) {
|
||||||
val migRecyler = migrationFrameLayout.recycler
|
|
||||||
if (migAdapter !is SourceAdapter) {
|
if (migAdapter !is SourceAdapter) {
|
||||||
migAdapter = SourceAdapter(this)
|
migAdapter = SourceAdapter(this)
|
||||||
migRecyler.adapter = migAdapter
|
migrationFrameLayout?.onBind(migAdapter!!)
|
||||||
migAdapter?.fastScroller = migrationFrameLayout.fastScroller
|
migAdapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
|
||||||
}
|
}
|
||||||
migAdapter?.updateDataSet(sources, true)
|
migAdapter?.updateDataSet(sources, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setMigrationManga(manga: List<MangaItem>?) {
|
fun setMigrationManga(manga: List<MangaItem>?) {
|
||||||
val migRecyler = migrationFrameLayout.recycler
|
|
||||||
if (migAdapter !is MangaAdapter) {
|
if (migAdapter !is MangaAdapter) {
|
||||||
migAdapter = MangaAdapter(this)
|
migAdapter = MangaAdapter(this)
|
||||||
migRecyler.adapter = migAdapter
|
migrationFrameLayout?.onBind(migAdapter!!)
|
||||||
migAdapter?.fastScroller = migrationFrameLayout.fastScroller
|
migAdapter?.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
|
||||||
}
|
}
|
||||||
migAdapter?.updateDataSet(manga, true)
|
migAdapter?.updateDataSet(manga, true)
|
||||||
}
|
}
|
||||||
@ -301,14 +299,7 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
presenter.uninstallExtension(pkgName)
|
presenter.uninstallExtension(pkgName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class TabbedSheetAdapter : ViewPagerAdapter() {
|
private inner class TabbedSheetAdapter : RecyclerViewPagerAdapter() {
|
||||||
|
|
||||||
override fun createView(container: ViewGroup, position: Int): View {
|
|
||||||
return when (position) {
|
|
||||||
0 -> extensionFrameLayout.root
|
|
||||||
else -> migrationFrameLayout.root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCount(): Int {
|
override fun getCount(): Int {
|
||||||
return 2
|
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()
|
getRecents()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
private companion object {
|
||||||
var lastRecents: List<RecentMangaItem>? = null
|
var lastRecents: List<RecentMangaItem>? = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import android.view.ViewGroup
|
|||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||||
import com.bluelinelabs.conductor.ControllerChangeType
|
import com.bluelinelabs.conductor.ControllerChangeType
|
||||||
import com.bluelinelabs.conductor.RouterTransaction
|
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.databinding.BrowseControllerBinding
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.Source
|
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.extension.SettingsExtensionsController
|
||||||
import eu.kanade.tachiyomi.ui.main.BottomSheetController
|
import eu.kanade.tachiyomi.ui.main.BottomSheetController
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
@ -66,7 +67,7 @@ import kotlin.math.min
|
|||||||
* [SourceAdapter.OnLatestClickListener] call function data on latest item click
|
* [SourceAdapter.OnLatestClickListener] call function data on latest item click
|
||||||
*/
|
*/
|
||||||
class BrowseController :
|
class BrowseController :
|
||||||
NucleusController<BrowseControllerBinding, SourcePresenter>(),
|
BaseController<BrowseControllerBinding>(),
|
||||||
FlexibleAdapter.OnItemClickListener,
|
FlexibleAdapter.OnItemClickListener,
|
||||||
SourceAdapter.SourceListener,
|
SourceAdapter.SourceListener,
|
||||||
RootSearchInterface,
|
RootSearchInterface,
|
||||||
@ -109,9 +110,7 @@ class BrowseController :
|
|||||||
} else view?.context?.getString(R.string.browse)
|
} else view?.context?.getString(R.string.browse)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createPresenter(): SourcePresenter {
|
val presenter = SourcePresenter(this)
|
||||||
return SourcePresenter()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createBinding(inflater: LayoutInflater) = BrowseControllerBinding.inflate(inflater)
|
override fun createBinding(inflater: LayoutInflater) = BrowseControllerBinding.inflate(inflater)
|
||||||
|
|
||||||
@ -119,29 +118,34 @@ class BrowseController :
|
|||||||
super.onViewCreated(view)
|
super.onViewCreated(view)
|
||||||
|
|
||||||
adapter = SourceAdapter(this)
|
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.sourceRecycler.adapter = adapter
|
||||||
binding.recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context)
|
|
||||||
binding.recycler.adapter = adapter
|
|
||||||
adapter?.isSwipeEnabled = true
|
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 attrsArray = intArrayOf(android.R.attr.actionBarSize)
|
||||||
val array = view.context.obtainStyledAttributes(attrsArray)
|
val array = view.context.obtainStyledAttributes(attrsArray)
|
||||||
val appBarHeight = array.getDimensionPixelSize(0, 0)
|
val appBarHeight = array.getDimensionPixelSize(0, 0)
|
||||||
array.recycle()
|
array.recycle()
|
||||||
scrollViewWith(
|
scrollViewWith(
|
||||||
binding.recycler,
|
binding.sourceRecycler,
|
||||||
afterInsets = {
|
afterInsets = {
|
||||||
headerHeight = it.systemWindowInsetTop + appBarHeight
|
headerHeight = it.systemWindowInsetTop + appBarHeight
|
||||||
binding.recycler.updatePaddingRelative(bottom = activityBinding?.bottomNav?.height ?: 0)
|
binding.sourceRecycler.updatePaddingRelative(bottom = activityBinding?.bottomNav?.height ?: 0)
|
||||||
},
|
},
|
||||||
onBottomNavUpdate = {
|
onBottomNavUpdate = {
|
||||||
setBottomPadding()
|
setBottomPadding()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.recycler.post {
|
binding.sourceRecycler.post {
|
||||||
setBottomSheetTabs(if (binding.bottomSheet.root.sheetBehavior.isCollapsed()) 0f else 1f)
|
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)
|
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301)
|
||||||
@ -154,7 +158,7 @@ class BrowseController :
|
|||||||
binding.shadow2.alpha = (1 - max(0f, progress)) * 0.25f
|
binding.shadow2.alpha = (1 - max(0f, progress)) * 0.25f
|
||||||
activityBinding?.appBar?.elevation = min(
|
activityBinding?.appBar?.elevation = min(
|
||||||
(1f - progress) * 15f,
|
(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))
|
activityBinding?.appBar?.y = max(activityBinding!!.appBar.y, -headerHeight * (1 - progress))
|
||||||
val oldShow = showingExtensions
|
val oldShow = showingExtensions
|
||||||
@ -200,6 +204,10 @@ class BrowseController :
|
|||||||
if (showingExtensions) {
|
if (showingExtensions) {
|
||||||
binding.bottomSheet.root.sheetBehavior?.expand()
|
binding.bottomSheet.root.sheetBehavior?.expand()
|
||||||
}
|
}
|
||||||
|
presenter.onCreate()
|
||||||
|
if (presenter.sourceItems.isNotEmpty()) {
|
||||||
|
setSources(presenter.sourceItems, presenter.lastUsedItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateTitleAndMenu() {
|
fun updateTitleAndMenu() {
|
||||||
@ -256,10 +264,10 @@ class BrowseController :
|
|||||||
)
|
)
|
||||||
binding.shadow2.translationY = pad
|
binding.shadow2.translationY = pad
|
||||||
binding.bottomSheet.root.sheetBehavior?.peekHeight = 58.spToPx + padding
|
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()
|
bottomMargin = -pad.toInt()
|
||||||
}
|
}
|
||||||
binding.bottomSheet.root.migrationFrameLayout.fastScroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
binding.bottomSheet.root.migrationFrameLayout?.binding?.fastScroller?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
bottomMargin = -pad.toInt()
|
bottomMargin = -pad.toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,6 +301,11 @@ class BrowseController :
|
|||||||
super.onDestroyView(view)
|
super.onDestroyView(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
presenter.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||||
super.onChangeStarted(handler, type)
|
super.onChangeStarted(handler, type)
|
||||||
if (!type.isPush) {
|
if (!type.isPush) {
|
||||||
@ -305,7 +318,7 @@ class BrowseController :
|
|||||||
activityBinding?.appBar?.elevation =
|
activityBinding?.appBar?.elevation =
|
||||||
when {
|
when {
|
||||||
binding.bottomSheet.root.sheetBehavior.isExpanded() -> 0f
|
binding.bottomSheet.root.sheetBehavior.isExpanded() -> 0f
|
||||||
binding.recycler.canScrollVertically(-1) -> 15f
|
binding.sourceRecycler.canScrollVertically(-1) -> 15f
|
||||||
else -> 0f
|
else -> 0f
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -483,8 +496,9 @@ class BrowseController :
|
|||||||
/**
|
/**
|
||||||
* Called to update adapter containing sources.
|
* Called to update adapter containing sources.
|
||||||
*/
|
*/
|
||||||
fun setSources(sources: List<IFlexible<*>>) {
|
fun setSources(sources: List<IFlexible<*>>, lastUsed: SourceItem?) {
|
||||||
adapter?.updateDataSet(sources)
|
adapter?.updateDataSet(sources, false)
|
||||||
|
setLastUsedSource(lastUsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package eu.kanade.tachiyomi.ui.source
|
package eu.kanade.tachiyomi.ui.source
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
import eu.kanade.tachiyomi.util.system.withUIContext
|
||||||
import rx.Observable
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import rx.Subscription
|
import kotlinx.coroutines.Dispatchers
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
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.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.TreeMap
|
import java.util.TreeMap
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presenter of [BrowseController]
|
* Presenter of [BrowseController]
|
||||||
@ -23,86 +24,102 @@ import java.util.concurrent.TimeUnit
|
|||||||
* @param preferences application preferences.
|
* @param preferences application preferences.
|
||||||
*/
|
*/
|
||||||
class SourcePresenter(
|
class SourcePresenter(
|
||||||
|
val controller: BrowseController,
|
||||||
val sourceManager: SourceManager = Injekt.get(),
|
val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val preferences: PreferencesHelper = Injekt.get()
|
private val preferences: PreferencesHelper = Injekt.get()
|
||||||
) : BasePresenter<BrowseController>() {
|
) {
|
||||||
|
|
||||||
|
private var scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||||
var sources = getEnabledSources()
|
var sources = getEnabledSources()
|
||||||
|
|
||||||
/**
|
var sourceItems = emptyList<SourceItem>()
|
||||||
* Subscription for retrieving enabled sources.
|
var lastUsedItem: SourceItem? = null
|
||||||
*/
|
|
||||||
private var sourceSubscription: Subscription? = null
|
|
||||||
private var lastUsedSubscription: Subscription? = null
|
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
var lastUsedJob: Job? = null
|
||||||
super.onCreate(savedState)
|
|
||||||
|
fun onCreate() {
|
||||||
|
if (lastSources != null) {
|
||||||
|
if (sourceItems.isEmpty()) {
|
||||||
|
sourceItems = lastSources ?: emptyList()
|
||||||
|
}
|
||||||
|
lastUsedItem = lastUsedItemRem
|
||||||
|
lastSources = null
|
||||||
|
lastUsedItemRem = null
|
||||||
|
}
|
||||||
|
|
||||||
// Load enabled and last used sources
|
// Load enabled and last used sources
|
||||||
loadSources()
|
loadSources()
|
||||||
loadLastUsedSource()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsubscribe and create a new subscription to fetch enabled sources.
|
* Unsubscribe and create a new subscription to fetch enabled sources.
|
||||||
*/
|
*/
|
||||||
private fun loadSources() {
|
private fun loadSources() {
|
||||||
sourceSubscription?.unsubscribe()
|
scope.launch {
|
||||||
|
val pinnedSources = mutableListOf<SourceItem>()
|
||||||
|
val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault()
|
||||||
|
|
||||||
val pinnedSources = mutableListOf<SourceItem>()
|
val map = TreeMap<String, MutableList<CatalogueSource>> { d1, d2 ->
|
||||||
val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault()
|
// Catalogues without a lang defined will be placed at the end
|
||||||
|
when {
|
||||||
val map = TreeMap<String, MutableList<CatalogueSource>> { d1, d2 ->
|
d1 == "" && d2 != "" -> 1
|
||||||
// Catalogues without a lang defined will be placed at the end
|
d2 == "" && d1 != "" -> -1
|
||||||
when {
|
else -> d1.compareTo(d2)
|
||||||
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 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() {
|
private fun loadLastUsedSource() {
|
||||||
lastUsedSubscription?.unsubscribe()
|
lastUsedJob?.cancel()
|
||||||
val sharedObs = preferences.lastUsedCatalogueSource().asObservable().share()
|
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.
|
private fun getLastUsedSource(value: Long): SourceItem? {
|
||||||
lastUsedSubscription = Observable.merge(
|
return (sourceManager.get(value) as? CatalogueSource)?.let { source ->
|
||||||
sharedObs.take(1),
|
val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault()
|
||||||
sharedObs.skip(1).delay(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
val isPinned = source.id.toString() in pinnedCatalogues
|
||||||
).distinctUntilChanged().map {
|
if (isPinned) null
|
||||||
(sourceManager.get(it) as? CatalogueSource)?.let { source ->
|
else SourceItem(source, null, isPinned)
|
||||||
val pinnedCatalogues = preferences.pinnedCatalogues().getOrDefault()
|
}
|
||||||
val isPinned = source.id.toString() in pinnedCatalogues
|
|
||||||
if (isPinned) null
|
|
||||||
else SourceItem(source, null, isPinned)
|
|
||||||
}
|
|
||||||
}.subscribeLatestCache(BrowseController::setLastUsedSource)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSources() {
|
fun updateSources() {
|
||||||
sources = getEnabledSources()
|
sources = getEnabledSources()
|
||||||
loadSources()
|
loadSources()
|
||||||
loadLastUsedSource()
|
}
|
||||||
|
|
||||||
|
fun onDestroy() {
|
||||||
|
lastSources = sourceItems
|
||||||
|
lastUsedItemRem = lastUsedItem
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,5 +141,8 @@ class SourcePresenter(
|
|||||||
companion object {
|
companion object {
|
||||||
const val PINNED_KEY = "pinned"
|
const val PINNED_KEY = "pinned"
|
||||||
const val LAST_USED_KEY = "last_used"
|
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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/browse_layout"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
@ -12,7 +13,7 @@
|
|||||||
android:background="?android:attr/colorBackground">
|
android:background="?android:attr/colorBackground">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recycler"
|
android:id="@+id/source_recycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/recycler_layout"
|
android:id="@+id/recycler_layout"
|
||||||
@ -21,4 +21,4 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:fastScrollerBubbleEnabled="false" />
|
app:fastScrollerBubbleEnabled="false" />
|
||||||
|
|
||||||
</FrameLayout>
|
</eu.kanade.tachiyomi.ui.extension.RecyclerWithScrollerView>
|
||||||
|
Loading…
Reference in New Issue
Block a user