From 23f5ac0fb96f67297dd979f07980b420ccc23dd3 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Thu, 8 Aug 2019 16:42:52 -0400 Subject: [PATCH] For #4066: Create InflationAwareFeature for lazy inflation --- .../fenix/browser/BaseBrowserFragment.kt | 2 +- .../fenix/components/FindInPageIntegration.kt | 51 +++------ .../fenix/components/InflationAwareFeature.kt | 84 ++++++++++++++ app/src/main/res/layout/fragment_browser.xml | 6 +- .../components/InflationAwareFeatureTest.kt | 107 ++++++++++++++++++ 5 files changed, 209 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/components/InflationAwareFeature.kt create mode 100644 app/src/test/java/org/mozilla/fenix/components/InflationAwareFeatureTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index 7c5446cd2..948ab60b6 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -183,7 +183,7 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs feature = FindInPageIntegration( sessionManager = requireComponents.core.sessionManager, sessionId = customTabSessionId, - view = view.findInPageView, + stub = view.stubFindInPage, engineView = view.engineView, toolbar = toolbar ), diff --git a/app/src/main/java/org/mozilla/fenix/components/FindInPageIntegration.kt b/app/src/main/java/org/mozilla/fenix/components/FindInPageIntegration.kt index d1dcfe1f3..ef8f724d9 100644 --- a/app/src/main/java/org/mozilla/fenix/components/FindInPageIntegration.kt +++ b/app/src/main/java/org/mozilla/fenix/components/FindInPageIntegration.kt @@ -5,11 +5,9 @@ package org.mozilla.fenix.components import android.content.Context -import android.os.Looper import android.util.AttributeSet import android.view.View import android.view.ViewStub -import androidx.annotation.UiThread import androidx.coordinatorlayout.widget.CoordinatorLayout import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.runWithSessionIdOrSelected @@ -18,54 +16,31 @@ import mozilla.components.concept.engine.EngineView import mozilla.components.feature.findinpage.FindInPageFeature import mozilla.components.feature.findinpage.view.FindInPageBar import mozilla.components.feature.findinpage.view.FindInPageView -import mozilla.components.support.base.feature.BackHandler import mozilla.components.support.base.feature.LifecycleAwareFeature import org.mozilla.fenix.test.Mockable -/** - * This class provides lazy loading of the Find in Page View. - * It should be launched on the app's UI thread. - */ @Mockable class FindInPageIntegration( private val sessionManager: SessionManager, private val sessionId: String? = null, - private val stub: ViewStub, - engineView: EngineView, + stub: ViewStub, + private val engineView: EngineView, private val toolbar: BrowserToolbar -) : LifecycleAwareFeature, BackHandler { - - private var view: FindInPageView? = null - private val feature: FindInPageFeature by lazy(LazyThreadSafetyMode.NONE) { - view = stub.inflate() as FindInPageView - FindInPageFeature(sessionManager, view!!, engineView, ::onClose).also { it.start() } +) : InflationAwareFeature(stub) { + override fun onViewInflated(view: View): LifecycleAwareFeature { + return FindInPageFeature(sessionManager, view as FindInPageView, engineView) { + toolbar.visibility = View.VISIBLE + view.visibility = View.GONE + } } - override fun start() { - } - - override fun stop() { - if (view != null) feature.stop() - } - - override fun onBackPressed(): Boolean { - return if (view != null) feature.onBackPressed() else false - } - - private fun onClose() { - toolbar.visibility = View.VISIBLE - view?.asView()?.visibility = View.GONE - } - - @UiThread - fun launch() { - require(Looper.myLooper() == Looper.getMainLooper()) { "This method should be run on the main UI thread." } - sessionManager.runWithSessionIdOrSelected(sessionId) { - if (!it.isCustomTabSession()) { + override fun onLaunch(view: View, feature: LifecycleAwareFeature) { + sessionManager.runWithSessionIdOrSelected(sessionId) { session -> + if (!session.isCustomTabSession()) { toolbar.visibility = View.GONE } - feature.bind(it) - view?.asView()?.visibility = View.VISIBLE + view.visibility = View.VISIBLE + (feature as FindInPageFeature).bind(session) } } } diff --git a/app/src/main/java/org/mozilla/fenix/components/InflationAwareFeature.kt b/app/src/main/java/org/mozilla/fenix/components/InflationAwareFeature.kt new file mode 100644 index 000000000..c1cefa6b0 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/components/InflationAwareFeature.kt @@ -0,0 +1,84 @@ +package org.mozilla.fenix.components + +import android.view.View +import android.view.ViewStub +import androidx.annotation.UiThread +import mozilla.components.support.base.feature.BackHandler +import mozilla.components.support.base.feature.LifecycleAwareFeature +import java.lang.ref.WeakReference + +/** + * A base feature class that enables lazy inflation of a view needed by a feature. + * + * When a feature needs to be launched (e.g. by user interaction) calling [launch] + * will inflate the view only then, start the feature, and then executes [onLaunch] + * for any feature-specific startup needs. + */ +abstract class InflationAwareFeature( + private val stub: ViewStub +) : LifecycleAwareFeature, BackHandler { + + internal lateinit var view: WeakReference + internal var feature: LifecycleAwareFeature? = null + private val stubListener = ViewStub.OnInflateListener { _, inflated -> + view = WeakReference(inflated) + feature = onViewInflated(inflated).also { + it.start() + onLaunch(inflated, it) + } + } + + /** + * Invoked when a view-dependent feature needs to be started along with the feature itself. + */ + @UiThread + fun launch() { + // If we have a feature and view, we can launch immediately. + if (feature != null && view.get() != null) { + onLaunch(view.get()!!, feature!!) + } else { + stub.apply { + setOnInflateListener(stubListener) + inflate() + } + } + } + + /** + * Implementation notes: This implemented method does nothing since we only start the feature + * when the view is inflated. + */ + override fun start() { + // We don't do anything because we only want to start the feature when it's being used. + } + + override fun stop() { + feature?.stop() + } + + /** + * Called when the feature gets the option to handle the user pressing the back key. + * + * @return true if the feature also implements [BackHandler] and the feature has been initiated. + */ + override fun onBackPressed(): Boolean { + return (feature as? BackHandler)?.onBackPressed() ?: false + } + + /** + * Invoked when the view has been inflated for the feature to be created with it. + * + * @param view The newly created view. + * @return The feature initiated with the view. + */ + abstract fun onViewInflated(view: View): LifecycleAwareFeature + + /** + * Invoked after the feature is instantiated. If the feature already exists, + * this is invoked immediately. + * + * @param view The view that is attached to the feature. + * @param feature The feature that was instantiated. + */ + abstract fun onLaunch(view: View, feature: LifecycleAwareFeature) +} diff --git a/app/src/main/res/layout/fragment_browser.xml b/app/src/main/res/layout/fragment_browser.xml index 46026dcf6..480c0b0a2 100644 --- a/app/src/main/res/layout/fragment_browser.xml +++ b/app/src/main/res/layout/fragment_browser.xml @@ -31,10 +31,12 @@ app:layout_behavior="org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior" /> + android:layout_gravity="bottom" + android:layout_width="match_parent" + android:layout_height="56dp" />