mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	MainActivity fixes (#6591)
* Reduce notifyDataSetChanged calls when category count is disabled * Fix category tabs briefly showing when it's supposed to be disabled Also fix tabs showing when activity recreated * Lift appbar when tab is hidden Check against tab visibility instead of viewpager * Restore selected nav item after recreate * Simplify SHORTCUT_MANGA intent handling Don't need to change controller if the topmost controller is the target
This commit is contained in:
		@@ -4,7 +4,10 @@ import com.google.android.material.tabs.TabLayout
 | 
			
		||||
 | 
			
		||||
interface TabbedController {
 | 
			
		||||
 | 
			
		||||
    fun configureTabs(tabs: TabLayout) {}
 | 
			
		||||
    /**
 | 
			
		||||
     * @return true to let activity updates tabs visibility (to visible)
 | 
			
		||||
     */
 | 
			
		||||
    fun configureTabs(tabs: TabLayout): Boolean = true
 | 
			
		||||
 | 
			
		||||
    fun cleanupTabs(tabs: TabLayout) {}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -79,11 +79,12 @@ class BrowseController :
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun configureTabs(tabs: TabLayout) {
 | 
			
		||||
    override fun configureTabs(tabs: TabLayout): Boolean {
 | 
			
		||||
        with(tabs) {
 | 
			
		||||
            tabGravity = TabLayout.GRAVITY_FILL
 | 
			
		||||
            tabMode = TabLayout.MODE_FIXED
 | 
			
		||||
        }
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun cleanupTabs(tabs: TabLayout) {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,24 +28,13 @@ class LibraryAdapter(
 | 
			
		||||
     * The categories to bind in the adapter.
 | 
			
		||||
     */
 | 
			
		||||
    var categories: List<Category> = emptyList()
 | 
			
		||||
        // This setter helps to not refresh the adapter if the reference to the list doesn't change.
 | 
			
		||||
        set(value) {
 | 
			
		||||
            if (field !== value) {
 | 
			
		||||
                field = value
 | 
			
		||||
                notifyDataSetChanged()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The number of manga in each category.
 | 
			
		||||
     * List order must be the same as [categories]
 | 
			
		||||
     */
 | 
			
		||||
    var itemsPerCategory: Map<Int, Int> = emptyMap()
 | 
			
		||||
        set(value) {
 | 
			
		||||
            if (field !== value) {
 | 
			
		||||
                field = value
 | 
			
		||||
                notifyDataSetChanged()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    private var itemsPerCategory: List<Int> = emptyList()
 | 
			
		||||
 | 
			
		||||
    private var boundViews = arrayListOf<View>()
 | 
			
		||||
 | 
			
		||||
@@ -62,6 +51,29 @@ class LibraryAdapter(
 | 
			
		||||
            .launchIn(controller.viewScope)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pair of category and size of category
 | 
			
		||||
     */
 | 
			
		||||
    fun updateCategories(new: List<Pair<Category, Int>>) {
 | 
			
		||||
        var updated = false
 | 
			
		||||
 | 
			
		||||
        val newCategories = new.map { it.first }
 | 
			
		||||
        if (categories != newCategories) {
 | 
			
		||||
            categories = newCategories
 | 
			
		||||
            updated = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val newItemsPerCategory = new.map { it.second }
 | 
			
		||||
        if (itemsPerCategory !== newItemsPerCategory) {
 | 
			
		||||
            itemsPerCategory = newItemsPerCategory
 | 
			
		||||
            updated = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (updated) {
 | 
			
		||||
            notifyDataSetChanged()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new view for this adapter.
 | 
			
		||||
     *
 | 
			
		||||
@@ -112,10 +124,11 @@ class LibraryAdapter(
 | 
			
		||||
     * @return the title to display.
 | 
			
		||||
     */
 | 
			
		||||
    override fun getPageTitle(position: Int): CharSequence {
 | 
			
		||||
        if (preferences.categoryNumberOfItems().get()) {
 | 
			
		||||
            return categories[position].let { "${it.name} (${itemsPerCategory[it.id]})" }
 | 
			
		||||
        return if (!preferences.categoryNumberOfItems().get()) {
 | 
			
		||||
            categories[position].name
 | 
			
		||||
        } else {
 | 
			
		||||
            categories[position].let { "${it.name} (${itemsPerCategory[position]})" }
 | 
			
		||||
        }
 | 
			
		||||
        return categories[position].name
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import android.view.MenuInflater
 | 
			
		||||
import android.view.MenuItem
 | 
			
		||||
import android.view.View
 | 
			
		||||
import androidx.appcompat.view.ActionMode
 | 
			
		||||
import androidx.core.view.doOnAttach
 | 
			
		||||
import androidx.core.view.isVisible
 | 
			
		||||
import com.bluelinelabs.conductor.ControllerChangeHandler
 | 
			
		||||
import com.bluelinelabs.conductor.ControllerChangeType
 | 
			
		||||
@@ -234,8 +235,9 @@ class LibraryController(
 | 
			
		||||
        super.onDestroyView(view)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun configureTabs(tabs: TabLayout) {
 | 
			
		||||
    override fun configureTabs(tabs: TabLayout): Boolean {
 | 
			
		||||
        with(tabs) {
 | 
			
		||||
            isVisible = false
 | 
			
		||||
            tabGravity = TabLayout.GRAVITY_START
 | 
			
		||||
            tabMode = TabLayout.MODE_SCROLLABLE
 | 
			
		||||
        }
 | 
			
		||||
@@ -247,6 +249,8 @@ class LibraryController(
 | 
			
		||||
        mangaCountVisibilitySubscription = mangaCountVisibilityRelay.subscribe {
 | 
			
		||||
            adapter?.notifyDataSetChanged()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun cleanupTabs(tabs: TabLayout) {
 | 
			
		||||
@@ -291,22 +295,17 @@ class LibraryController(
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set the categories
 | 
			
		||||
        adapter.categories = categories
 | 
			
		||||
        adapter.itemsPerCategory = adapter.categories
 | 
			
		||||
            .map { (it.id ?: -1) to (mangaMap[it.id]?.size ?: 0) }
 | 
			
		||||
            .toMap()
 | 
			
		||||
        adapter.updateCategories(categories.map { it to (mangaMap[it.id]?.size ?: 0) })
 | 
			
		||||
 | 
			
		||||
        // Restore active category.
 | 
			
		||||
        binding.libraryPager.setCurrentItem(activeCat, false)
 | 
			
		||||
 | 
			
		||||
        // Trigger display of tabs
 | 
			
		||||
        onTabsSettingsChanged()
 | 
			
		||||
        onTabsSettingsChanged(firstLaunch = true)
 | 
			
		||||
 | 
			
		||||
        // Delay the scroll position to allow the view to be properly measured.
 | 
			
		||||
        view.post {
 | 
			
		||||
            if (isAttached) {
 | 
			
		||||
                (activity as? MainActivity)?.binding?.tabs?.setScrollPosition(binding.libraryPager.currentItem, 0f, true)
 | 
			
		||||
            }
 | 
			
		||||
        view.doOnAttach {
 | 
			
		||||
            (activity as? MainActivity)?.binding?.tabs?.setScrollPosition(binding.libraryPager.currentItem, 0f, true)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Send the manga map to child fragments after the adapter is updated.
 | 
			
		||||
@@ -338,9 +337,11 @@ class LibraryController(
 | 
			
		||||
        presenter.requestBadgesUpdate()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun onTabsSettingsChanged() {
 | 
			
		||||
    private fun onTabsSettingsChanged(firstLaunch: Boolean = false) {
 | 
			
		||||
        if (!firstLaunch) {
 | 
			
		||||
            mangaCountVisibilityRelay.call(preferences.categoryNumberOfItems().get())
 | 
			
		||||
        }
 | 
			
		||||
        tabsVisibilityRelay.call(preferences.categoryTabs().get() && adapter?.categories?.size ?: 0 > 1)
 | 
			
		||||
        mangaCountVisibilityRelay.call(preferences.categoryNumberOfItems().get())
 | 
			
		||||
        updateTitle()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import com.bluelinelabs.conductor.Conductor
 | 
			
		||||
import com.bluelinelabs.conductor.Controller
 | 
			
		||||
import com.bluelinelabs.conductor.ControllerChangeHandler
 | 
			
		||||
import com.bluelinelabs.conductor.Router
 | 
			
		||||
import com.bluelinelabs.conductor.RouterTransaction
 | 
			
		||||
import com.google.android.material.navigation.NavigationBarView
 | 
			
		||||
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
 | 
			
		||||
import dev.chrisbanes.insetter.applyInsetter
 | 
			
		||||
@@ -236,6 +237,11 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
 | 
			
		||||
            if (didMigration && !BuildConfig.DEBUG) {
 | 
			
		||||
                WhatsNewDialogController().showDialog(router)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // Restore selected nav item
 | 
			
		||||
            router.backstack.firstOrNull()?.tag()?.toIntOrNull()?.let {
 | 
			
		||||
                nav.menu.findItem(it).isChecked = true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        merge(preferences.showUpdatesNavBadge().asFlow(), preferences.unreadUpdatesCount().asFlow())
 | 
			
		||||
@@ -403,11 +409,12 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
 | 
			
		||||
            }
 | 
			
		||||
            SHORTCUT_MANGA -> {
 | 
			
		||||
                val extras = intent.extras ?: return false
 | 
			
		||||
                if (router.backstackSize > 1) {
 | 
			
		||||
                val fgController = router.backstack.last()?.controller as? MangaController
 | 
			
		||||
                if (fgController?.manga?.id != extras.getLong(MangaController.MANGA_EXTRA)) {
 | 
			
		||||
                    router.popToRoot()
 | 
			
		||||
                    setSelectedNavItem(R.id.nav_library)
 | 
			
		||||
                    router.pushController(RouterTransaction.with(MangaController(extras)))
 | 
			
		||||
                }
 | 
			
		||||
                setSelectedNavItem(R.id.nav_library)
 | 
			
		||||
                router.pushController(MangaController(extras).withFadeTransaction())
 | 
			
		||||
            }
 | 
			
		||||
            SHORTCUT_DOWNLOADS -> {
 | 
			
		||||
                if (router.backstackSize > 1) {
 | 
			
		||||
@@ -553,11 +560,12 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
 | 
			
		||||
            from.cleanupTabs(binding.tabs)
 | 
			
		||||
        }
 | 
			
		||||
        if (to is TabbedController) {
 | 
			
		||||
            to.configureTabs(binding.tabs)
 | 
			
		||||
            if (to.configureTabs(binding.tabs)) {
 | 
			
		||||
                binding.tabs.isVisible = true
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            binding.tabs.setupWithViewPager(null)
 | 
			
		||||
            binding.tabs.isVisible = false
 | 
			
		||||
        }
 | 
			
		||||
        binding.tabs.isVisible = to is TabbedController
 | 
			
		||||
 | 
			
		||||
        if (from is FabController) {
 | 
			
		||||
            from.cleanupFab(binding.fabLayout.rootFab)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,23 +5,15 @@ import android.os.Parcel
 | 
			
		||||
import android.os.Parcelable
 | 
			
		||||
import android.util.AttributeSet
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import androidx.coordinatorlayout.R
 | 
			
		||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
 | 
			
		||||
import androidx.core.view.doOnLayout
 | 
			
		||||
import androidx.core.view.isVisible
 | 
			
		||||
import androidx.customview.view.AbsSavedState
 | 
			
		||||
import androidx.lifecycle.coroutineScope
 | 
			
		||||
import androidx.lifecycle.findViewTreeLifecycleOwner
 | 
			
		||||
import androidx.viewpager.widget.ViewPager
 | 
			
		||||
import com.bluelinelabs.conductor.ChangeHandlerFrameLayout
 | 
			
		||||
import com.google.android.material.appbar.AppBarLayout
 | 
			
		||||
import com.google.android.material.tabs.TabLayout
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.isTablet
 | 
			
		||||
import eu.kanade.tachiyomi.util.view.findChild
 | 
			
		||||
import eu.kanade.tachiyomi.util.view.findDescendant
 | 
			
		||||
import kotlinx.coroutines.flow.launchIn
 | 
			
		||||
import kotlinx.coroutines.flow.onEach
 | 
			
		||||
import reactivecircus.flowbinding.android.view.HierarchyChangeEvent
 | 
			
		||||
import reactivecircus.flowbinding.android.view.hierarchyChangeEvents
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * [CoordinatorLayout] with its own app bar lift state handler.
 | 
			
		||||
@@ -33,8 +25,6 @@ import reactivecircus.flowbinding.android.view.hierarchyChangeEvents
 | 
			
		||||
 * With those conditions, this view expects the following direct child:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. An [AppBarLayout].
 | 
			
		||||
 *
 | 
			
		||||
 * 2. A [ChangeHandlerFrameLayout] that contains an optional [ViewPager].
 | 
			
		||||
 */
 | 
			
		||||
class TachiyomiCoordinatorLayout @JvmOverloads constructor(
 | 
			
		||||
    context: Context,
 | 
			
		||||
@@ -48,7 +38,7 @@ class TachiyomiCoordinatorLayout @JvmOverloads constructor(
 | 
			
		||||
    private val isTablet = context.isTablet()
 | 
			
		||||
 | 
			
		||||
    private var appBarLayout: AppBarLayout? = null
 | 
			
		||||
    private var viewPager: ViewPager? = null
 | 
			
		||||
    private var tabLayout: TabLayout? = null
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * If true, [AppBarLayout] child will be lifted on nested scroll.
 | 
			
		||||
@@ -72,32 +62,21 @@ class TachiyomiCoordinatorLayout @JvmOverloads constructor(
 | 
			
		||||
    ) {
 | 
			
		||||
        super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)
 | 
			
		||||
        // Disable elevation overlay when tabs are visible
 | 
			
		||||
        if (canLiftAppBarOnScroll && viewPager == null) {
 | 
			
		||||
            appBarLayout?.isLifted = dyConsumed != 0 || dyUnconsumed >= 0
 | 
			
		||||
        if (canLiftAppBarOnScroll) {
 | 
			
		||||
            appBarLayout?.isLifted = (dyConsumed != 0 || dyUnconsumed >= 0) && tabLayout?.isVisible == false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onAttachedToWindow() {
 | 
			
		||||
        super.onAttachedToWindow()
 | 
			
		||||
        appBarLayout = findChild()
 | 
			
		||||
        viewPager = findChild<ChangeHandlerFrameLayout>()?.findDescendant()
 | 
			
		||||
 | 
			
		||||
        // Updates ViewPager reference when controller is changed
 | 
			
		||||
        findViewTreeLifecycleOwner()?.lifecycle?.coroutineScope?.let { scope ->
 | 
			
		||||
            findChild<ChangeHandlerFrameLayout>()?.hierarchyChangeEvents()
 | 
			
		||||
                ?.onEach {
 | 
			
		||||
                    if (it is HierarchyChangeEvent.ChildRemoved) {
 | 
			
		||||
                        viewPager = (it.parent as? ViewGroup)?.findDescendant()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                ?.launchIn(scope)
 | 
			
		||||
        }
 | 
			
		||||
        tabLayout = appBarLayout?.findChild()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDetachedFromWindow() {
 | 
			
		||||
        super.onDetachedFromWindow()
 | 
			
		||||
        appBarLayout = null
 | 
			
		||||
        viewPager = null
 | 
			
		||||
        tabLayout = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onSaveInstanceState(): Parcelable? {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user