From ab6e7dc52b3e281b735de064b02d81fb4cb1f8fe Mon Sep 17 00:00:00 2001 From: Mugurell Date: Tue, 20 Aug 2019 12:52:25 +0300 Subject: [PATCH] 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. --- .../org/mozilla/fenix/home/HomeFragment.kt | 24 ++++----- .../fenix/home/TransitionPreDrawListener.kt | 52 ------------------- .../fenix/utils/FragmentPreDrawManager.kt | 36 +++++++++++++ 3 files changed, 46 insertions(+), 66 deletions(-) delete mode 100644 app/src/main/java/org/mozilla/fenix/home/TransitionPreDrawListener.kt create mode 100644 app/src/main/java/org/mozilla/fenix/utils/FragmentPreDrawManager.kt diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 2f0da7fcb..692947a22 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -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,20 +183,6 @@ class HomeFragment : Fragment(), AccountObserver { val activity = activity as HomeActivity activity.themeManager.applyStatusBarTheme(activity) - postponeEnterTransition() - TransitionPreDrawListener( - fragment = this, - viewTreeObserver = sessionControlComponent.view.viewTreeObserver, - restoreLayoutState = { - val homeViewModel: HomeScreenViewModel by activityViewModels() - homeViewModel.layoutManagerState?.also { parcelable -> - sessionControlComponent.view.layoutManager?.onRestoreInstanceState(parcelable) - } - homeLayout?.progress = homeViewModel.motionLayoutProgress - homeViewModel.layoutManagerState = null - } - ) - return view } @@ -203,6 +190,15 @@ class HomeFragment : Fragment(), AccountObserver { 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) + } + homeLayout?.progress = homeViewModel.motionLayoutProgress + homeViewModel.layoutManagerState = null + } + setupHomeMenu() viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { diff --git a/app/src/main/java/org/mozilla/fenix/home/TransitionPreDrawListener.kt b/app/src/main/java/org/mozilla/fenix/home/TransitionPreDrawListener.kt deleted file mode 100644 index f7abb64f2..000000000 --- a/app/src/main/java/org/mozilla/fenix/home/TransitionPreDrawListener.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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 - -import android.view.ViewTreeObserver -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 { - - 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) - } - - override fun onPreDraw(): Boolean { - if (fragment.view != null) { - fragment.viewLifecycleOwner.lifecycleScope.launch { - delay(ANIM_SCROLL_DELAY) - restoreLayoutState() - fragment.startPostponedEnterTransition() - }.invokeOnCompletion { - viewTreeObserver.removeOnPreDrawListener(this) - } - } - return true - } - - companion object { - private const val ANIM_SCROLL_DELAY = 100L - } -} diff --git a/app/src/main/java/org/mozilla/fenix/utils/FragmentPreDrawManager.kt b/app/src/main/java/org/mozilla/fenix/utils/FragmentPreDrawManager.kt new file mode 100644 index 000000000..8d5066aae --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/utils/FragmentPreDrawManager.kt @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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.utils + +import androidx.core.view.doOnPreDraw +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +/** + * Helper class that allows executing code immediately before [Fragment]s View being drawn. + */ +class FragmentPreDrawManager( + private val fragment: Fragment +) { + init { + fragment.postponeEnterTransition() + } + + fun execute(code: () -> Unit) { + fragment.view?.doOnPreDraw { + fragment.viewLifecycleOwner.lifecycleScope.launch { + delay(ANIM_SCROLL_DELAY) + code() + fragment.startPostponedEnterTransition() + } + } + } + + companion object { + private const val ANIM_SCROLL_DELAY = 100L + } +}