ui: fix broken back navigation

Caused by a change in a dependency update that suddenly causes
FragmentManager's back callback to to jump ahead of ours for no
reason.
This commit is contained in:
Alexander Capehart 2023-07-25 19:51:08 -06:00
parent 6ef2f74694
commit 842677fab4
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47

View file

@ -26,7 +26,6 @@ import androidx.activity.OnBackPressedCallback
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.FragmentContainerView
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavDestination import androidx.navigation.NavDestination
@ -78,7 +77,6 @@ class MainFragment :
private var sheetBackCallback: SheetBackPressedCallback? = null private var sheetBackCallback: SheetBackPressedCallback? = null
private var detailBackCallback: DetailBackPressedCallback? = null private var detailBackCallback: DetailBackPressedCallback? = null
private var selectionBackCallback: SelectionBackPressedCallback? = null private var selectionBackCallback: SelectionBackPressedCallback? = null
private var exploreBackCallback: ExploreBackPressedCallback? = null
private var lastInsets: WindowInsets? = null private var lastInsets: WindowInsets? = null
private var elevationNormal = 0f private var elevationNormal = 0f
private var initialNavDestinationChange = true private var initialNavDestinationChange = true
@ -104,28 +102,17 @@ class MainFragment :
// Currently all back press callbacks are handled in MainFragment, as it's not guaranteed // Currently all back press callbacks are handled in MainFragment, as it's not guaranteed
// that instantiating these callbacks in their respective fragments would result in the // that instantiating these callbacks in their respective fragments would result in the
// correct order. // correct order.
val sheetBackCallback = sheetBackCallback =
SheetBackPressedCallback( SheetBackPressedCallback(
playbackSheetBehavior = playbackSheetBehavior, playbackSheetBehavior = playbackSheetBehavior,
queueSheetBehavior = queueSheetBehavior) queueSheetBehavior = queueSheetBehavior)
.also { sheetBackCallback = it }
val detailBackCallback = val detailBackCallback =
DetailBackPressedCallback(detailModel).also { detailBackCallback = it } DetailBackPressedCallback(detailModel).also { detailBackCallback = it }
val selectionBackCallback = val selectionBackCallback =
SelectionBackPressedCallback(listModel).also { selectionBackCallback = it } SelectionBackPressedCallback(listModel).also { selectionBackCallback = it }
val exploreBackCallback =
ExploreBackPressedCallback(binding.exploreNavHost).also { exploreBackCallback = it }
// --- UI SETUP --- // --- UI SETUP ---
val context = requireActivity() val context = requireActivity()
// Override the back pressed listener so we can map back navigation to collapsing
// navigation, navigation out of detail views, etc.
context.onBackPressedDispatcher.apply {
addCallback(viewLifecycleOwner, exploreBackCallback)
addCallback(viewLifecycleOwner, selectionBackCallback)
addCallback(viewLifecycleOwner, detailBackCallback)
addCallback(viewLifecycleOwner, sheetBackCallback)
}
binding.root.setOnApplyWindowInsetsListener { _, insets -> binding.root.setOnApplyWindowInsetsListener { _, insets ->
lastInsets = insets lastInsets = insets
@ -182,6 +169,18 @@ class MainFragment :
binding.playbackSheet.viewTreeObserver.addOnPreDrawListener(this@MainFragment) binding.playbackSheet.viewTreeObserver.addOnPreDrawListener(this@MainFragment)
} }
override fun onResume() {
super.onResume()
// Override the back pressed listener so we can map back navigation to collapsing
// navigation, navigation out of detail views, etc. We have to do this here in
// onResume or otherwise the FragmentManager will have precedence.
requireActivity().onBackPressedDispatcher.apply {
addCallback(viewLifecycleOwner, requireNotNull(selectionBackCallback))
addCallback(viewLifecycleOwner, requireNotNull(detailBackCallback))
addCallback(viewLifecycleOwner, requireNotNull(sheetBackCallback))
}
}
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
val binding = requireBinding() val binding = requireBinding()
@ -194,7 +193,6 @@ class MainFragment :
sheetBackCallback = null sheetBackCallback = null
detailBackCallback = null detailBackCallback = null
selectionBackCallback = null selectionBackCallback = null
exploreBackCallback = null
} }
override fun onPreDraw(): Boolean { override fun onPreDraw(): Boolean {
@ -296,8 +294,6 @@ class MainFragment :
// Drop the initial call by NavController that simply provides us with the current // Drop the initial call by NavController that simply provides us with the current
// destination. This would cause the selection state to be lost every time the device // destination. This would cause the selection state to be lost every time the device
// rotates. // rotates.
requireNotNull(exploreBackCallback) { "ExploreBackPressedCallback was not available" }
.invalidateEnabled()
if (!initialNavDestinationChange) { if (!initialNavDestinationChange) {
initialNavDestinationChange = true initialNavDestinationChange = true
return return
@ -486,23 +482,4 @@ class MainFragment :
isEnabled = selection.isNotEmpty() isEnabled = selection.isNotEmpty()
} }
} }
private inner class ExploreBackPressedCallback(
private val exploreNavHost: FragmentContainerView
) : OnBackPressedCallback(false) {
// Note: We cannot cache the NavController in a variable since it's current destination
// value goes stale for some reason.
override fun handleOnBackPressed() {
exploreNavHost.findNavController().navigateUp()
logD("Forwarded back navigation to explore nav host")
}
fun invalidateEnabled() {
val exploreNavController = exploreNavHost.findNavController()
isEnabled =
exploreNavController.currentDestination?.id !=
exploreNavController.graph.startDestinationId
}
}
} }