mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +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