Search code examples
androidkotlinandroidxandroid-preferences

Bug of OnBackPressedDispatcher in androidx preference


There are chain MainFragment > SettingsFragment > SubSettingsFragment:

navController.navigate(MainFragment.toSettingsFragment())
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <Preference
        android:fragment=".SubSettingsFragment"
        app:summary="@string/pref_header_summary_permission"
        app:title="@string/pref_title_permission" />

</PreferenceScreen>

When we located at SubSettingsFragment and press back button, appears MainFragment. SettingsFragment skipped.

PreferenceHeaderFragmentCompat has onBackPressedCallback handle accoding source code. But for some reason don't work.

Can we connect backstack of NavController to PreferenceHeaderFragmentCompat?

Update 1

I found why onBackPressedCallback don't work. In official source code in onViewCreated, dispatcher from context is null. That's why addCallback isn't run.

    @CallSuper
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        onBackPressedCallback = InnerOnBackPressedCallback(this)
        slidingPaneLayout.doOnLayout {
            onBackPressedCallback!!.isEnabled =
                slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen
        }
        childFragmentManager.addOnBackStackChangedListener {
            onBackPressedCallback!!.isEnabled = childFragmentManager.backStackEntryCount == 0
        }

        // this owner is null
        val onBackPressedDispatcherOwner = requireContext() as? OnBackPressedDispatcherOwner
        onBackPressedDispatcherOwner?.let {
            it.onBackPressedDispatcher.addCallback(
                viewLifecycleOwner,
                onBackPressedCallback!!
            )
        }
    }

I create custom PreferenceHeaderFragmentCompat where change requireContext() to requireActivity() and it's works. Back pressed from SubSettingsFragment pop to SettingsFragment.

requireActivity().onBackPressedDispatcher.addCallback(
    viewLifecycleOwner,
    onBackPressedCallback!!
)

Do you know why requireContext() as? OnBackPressedDispatcherOwner is null?


Solution

  • The problem was in onViewCreated() of official PreferenceHeaderFragmentCompat. Fixed by Google for future release (see issue).

    // this owner is null when we use Hilt annotation - AndroidEntryPoint 
    val onBackPressedDispatcherOwner = requireContext() as? OnBackPressedDispatcherOwner
    onBackPressedDispatcherOwner?.let {
        it.onBackPressedDispatcher.addCallback(
            viewLifecycleOwner,
            onBackPressedCallback!!
        )
    }