Commit ab6e7dc5 authored by Mugurell's avatar Mugurell Committed by Sebastian Kaspari
Browse files

Fix 4827 - Use ktx doOnPreDraw() to restore HomeFragment's layout

The previous solution would result in a crash because the passed in
viewTreeObserver that would trigger onPreDraw would be invalid.
The proposed solution is simpler and ensures we'll always use the right
viewTreeObserver.

`FragmentPreDrawManager` is general enough that can be used by other Fragments
also, so I've added it to the `utils` package.
parent 115833c7
Loading
Loading
Loading
Loading
+10 −14
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.onboarding.FenixOnboarding
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.share.ShareTab
import org.mozilla.fenix.utils.FragmentPreDrawManager
import org.mozilla.fenix.utils.allowUndo

@SuppressWarnings("TooManyFunctions", "LargeClass")
@@ -182,11 +183,14 @@ class HomeFragment : Fragment(), AccountObserver {
        val activity = activity as HomeActivity
        activity.themeManager.applyStatusBarTheme(activity)

        postponeEnterTransition()
        TransitionPreDrawListener(
            fragment = this,
            viewTreeObserver = sessionControlComponent.view.viewTreeObserver,
            restoreLayoutState = {
        return view
    }

    @SuppressWarnings("LongMethod")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        FragmentPreDrawManager(this).execute {
            val homeViewModel: HomeScreenViewModel by activityViewModels()
            homeViewModel.layoutManagerState?.also { parcelable ->
                sessionControlComponent.view.layoutManager?.onRestoreInstanceState(parcelable)
@@ -194,14 +198,6 @@ class HomeFragment : Fragment(), AccountObserver {
            homeLayout?.progress = homeViewModel.motionLayoutProgress
            homeViewModel.layoutManagerState = null
        }
        )

        return view
    }

    @SuppressWarnings("LongMethod")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        setupHomeMenu()

+36 −0
Original line number Diff line number Diff line
@@ -2,48 +2,32 @@
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.fenix.home
package org.mozilla.fenix.utils

import android.view.ViewTreeObserver
import androidx.core.view.doOnPreDraw
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class TransitionPreDrawListener(
    private val fragment: Fragment,
    private val viewTreeObserver: ViewTreeObserver,
    private val restoreLayoutState: () -> Unit
) : ViewTreeObserver.OnPreDrawListener, LifecycleObserver {

/**
 * Helper class that allows executing code immediately before [Fragment]s View being drawn.
 */
class FragmentPreDrawManager(
    private val fragment: Fragment
) {
    init {
        fragment.viewLifecycleOwner.lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun onCreateView() {
        viewTreeObserver.addOnPreDrawListener(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroyView() {
        viewTreeObserver.removeOnPreDrawListener(this)
        fragment.postponeEnterTransition()
    }

    override fun onPreDraw(): Boolean {
        if (fragment.view != null) {
    fun execute(code: () -> Unit) {
        fragment.view?.doOnPreDraw {
            fragment.viewLifecycleOwner.lifecycleScope.launch {
                delay(ANIM_SCROLL_DELAY)
                restoreLayoutState()
                code()
                fragment.startPostponedEnterTransition()
            }.invokeOnCompletion {
                viewTreeObserver.removeOnPreDrawListener(this)
            }
        }
        return true
    }

    companion object {