ui: fix back listener leak

Fix an issue where the OnBackPressedListeners would leak their bound
views once MainFragment's view was destroyed.
This commit is contained in:
Alexander Capehart 2023-06-03 09:24:10 -06:00
parent 736f3ec6b7
commit d49034b664
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47

View file

@ -81,10 +81,10 @@ class MainFragment :
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
private val selectionModel: SelectionViewModel by activityViewModels() private val selectionModel: SelectionViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels() private val detailModel: DetailViewModel by activityViewModels()
private lateinit var sheetBackCallback: SheetBackPressedCallback private var sheetBackCallback: SheetBackPressedCallback? = null
private lateinit var detailBackCallback: DetailBackPressedCallback private var detailBackCallback: DetailBackPressedCallback? = null
private lateinit var selectionBackCallback: SelectionBackPressedCallback private var selectionBackCallback: SelectionBackPressedCallback? = null
private lateinit var exploreBackCallback: ExploreBackPressedCallback 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
@ -110,13 +110,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.
sheetBackCallback = val sheetBackCallback =
SheetBackPressedCallback( SheetBackPressedCallback(
playbackSheetBehavior = playbackSheetBehavior, playbackSheetBehavior = playbackSheetBehavior,
queueSheetBehavior = queueSheetBehavior) queueSheetBehavior = queueSheetBehavior)
detailBackCallback = DetailBackPressedCallback(detailModel) .also { sheetBackCallback = it }
selectionBackCallback = SelectionBackPressedCallback(selectionModel) val detailBackCallback =
exploreBackCallback = ExploreBackPressedCallback(binding.exploreNavHost) DetailBackPressedCallback(detailModel).also { detailBackCallback = it }
val selectionBackCallback =
SelectionBackPressedCallback(selectionModel).also { selectionBackCallback = it }
val exploreBackCallback =
ExploreBackPressedCallback(binding.exploreNavHost).also { exploreBackCallback = it }
// --- UI SETUP --- // --- UI SETUP ---
val context = requireActivity() val context = requireActivity()
@ -202,6 +206,14 @@ class MainFragment :
binding.playbackSheet.viewTreeObserver.removeOnPreDrawListener(this) binding.playbackSheet.viewTreeObserver.removeOnPreDrawListener(this)
} }
override fun onDestroyBinding(binding: FragmentMainBinding) {
super.onDestroyBinding(binding)
sheetBackCallback = null
detailBackCallback = null
selectionBackCallback = null
exploreBackCallback = null
}
override fun onPreDraw(): Boolean { override fun onPreDraw(): Boolean {
// We overload CoordinatorLayout far too much to rely on any of it's typical // We overload CoordinatorLayout far too much to rely on any of it's typical
// listener functionality. Just update all transitions before every draw. Should // listener functionality. Just update all transitions before every draw. Should
@ -287,7 +299,8 @@ class MainFragment :
// Since the navigation listener is also reliant on the bottom sheets, we must also update // Since the navigation listener is also reliant on the bottom sheets, we must also update
// it every frame. // it every frame.
sheetBackCallback.invalidateEnabled() requireNotNull(sheetBackCallback) { "SheetBackPressedCallback was not available" }
.invalidateEnabled()
return true return true
} }
@ -300,7 +313,8 @@ 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.
exploreBackCallback.invalidateEnabled() requireNotNull(exploreBackCallback) { "ExploreBackPressedCallback was not available" }
.invalidateEnabled()
if (!initialNavDestinationChange) { if (!initialNavDestinationChange) {
initialNavDestinationChange = true initialNavDestinationChange = true
return return