From 1c389d123154ebe2eb3260e36759b843403e7a5b Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Mon, 19 Aug 2019 19:27:43 -0400 Subject: [PATCH] [fenix] For https://github.com/mozilla-mobile/fenix/issues/4744 - Extract TransitionPreDrawListener (https://github.com/mozilla-mobile/fenix/pull/4812) --- .../org/mozilla/fenix/home/HomeFragment.kt | 39 +++++--------- .../fenix/home/TransitionPreDrawListener.kt | 52 +++++++++++++++++++ .../home/TransitionPreDrawListenerTest.kt | 41 +++++++++++++++ 3 files changed, 105 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/home/TransitionPreDrawListener.kt create mode 100644 app/src/test/java/org/mozilla/fenix/home/TransitionPreDrawListenerTest.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 b85e4cbabb..2f0da7fcb9 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -12,7 +12,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.ViewTreeObserver import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity @@ -111,21 +110,6 @@ class HomeFragment : Fragment(), AccountObserver { } } - private val preDrawListener = object : ViewTreeObserver.OnPreDrawListener { - override fun onPreDraw(): Boolean { - if (view != null) { - viewLifecycleOwner.lifecycleScope.launch { - delay(ANIM_SCROLL_DELAY) - restoreLayoutState() - startPostponedEnterTransition() - }.invokeOnCompletion { - sessionControlComponent.view.viewTreeObserver.removeOnPreDrawListener(this) - } - } - return true - } - } - private var homeMenu: HomeMenu? = null private val sessionManager: SessionManager @@ -199,20 +183,22 @@ class HomeFragment : Fragment(), AccountObserver { activity.themeManager.applyStatusBarTheme(activity) postponeEnterTransition() - sessionControlComponent.view.viewTreeObserver.addOnPreDrawListener(preDrawListener) + 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 } - private fun restoreLayoutState() { - val homeViewModel: HomeScreenViewModel by activityViewModels() - homeViewModel.layoutManagerState?.also { parcelable -> - sessionControlComponent.view.layoutManager?.onRestoreInstanceState(parcelable) - } - homeLayout?.progress = homeViewModel.motionLayoutProgress - homeViewModel.layoutManagerState = null - } - @SuppressWarnings("LongMethod") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -276,7 +262,6 @@ class HomeFragment : Fragment(), AccountObserver { override fun onDestroyView() { homeMenu = null - sessionControlComponent.view.viewTreeObserver.removeOnPreDrawListener(preDrawListener) super.onDestroyView() } diff --git a/app/src/main/java/org/mozilla/fenix/home/TransitionPreDrawListener.kt b/app/src/main/java/org/mozilla/fenix/home/TransitionPreDrawListener.kt new file mode 100644 index 0000000000..f7abb64f24 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/home/TransitionPreDrawListener.kt @@ -0,0 +1,52 @@ +/* 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/test/java/org/mozilla/fenix/home/TransitionPreDrawListenerTest.kt b/app/src/test/java/org/mozilla/fenix/home/TransitionPreDrawListenerTest.kt new file mode 100644 index 0000000000..6b24a5cafe --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/home/TransitionPreDrawListenerTest.kt @@ -0,0 +1,41 @@ +/* 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 io.mockk.mockk +import io.mockk.verify +import org.junit.Before +import org.junit.Test + +class TransitionPreDrawListenerTest { + + private lateinit var fragment: Fragment + private lateinit var viewTreeObserver: ViewTreeObserver + + @Before + fun setup() { + fragment = mockk(relaxed = true) + viewTreeObserver = mockk(relaxed = true) + } + + @Test + fun `adds observer when constructed`() { + val listener = TransitionPreDrawListener(fragment, viewTreeObserver) {} + verify { fragment.viewLifecycleOwner.lifecycle.addObserver(listener) } + } + + @Test + fun `adds listener on create and removes on destroy`() { + val listener = TransitionPreDrawListener(fragment, viewTreeObserver) {} + + listener.onCreateView() + verify { viewTreeObserver.addOnPreDrawListener(listener) } + + listener.onDestroyView() + verify { viewTreeObserver.removeOnPreDrawListener(listener) } + } +}