From b52091ed34b0c759f6a614e33be0db1e601d6ef2 Mon Sep 17 00:00:00 2001 From: MarcLeclair Date: Thu, 18 Jun 2020 19:43:20 -0400 Subject: [PATCH] For #11660: added prefetch for topsites and update in onCreateView() (#11668) * For #11660:added prefetch for topsites TopSites will be prefetched with observerOnce (wrapper around observerForever). Also, the SessionControlView.update() is called right away instead of waiting from consumeFrom in the HomeFragment.onCreateView() which will allow the UI to render all at once on its first perform traversal * Removed the submitList(null) since it retriggered a drawing on lower end device --- .../org/mozilla/fenix/FenixApplication.kt | 8 +++++++ .../fenix/components/TopSiteStorage.kt | 7 +++++++ .../java/org/mozilla/fenix/ext/LiveData.kt | 20 ++++++++++++++++++ .../org/mozilla/fenix/home/HomeFragment.kt | 21 +++++++++++++++---- .../home/sessioncontrol/SessionControlView.kt | 6 ------ 5 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/ext/LiveData.kt diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 48956ca45..3fa3a10e9 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -142,6 +142,7 @@ open class FenixApplication : LocaleAwareApplication() { } } + prefetchForHomeFragment() setupLeakCanary() if (settings().isTelemetryEnabled) { components.analytics.metrics.start(MetricServiceType.Data) @@ -228,6 +229,13 @@ open class FenixApplication : LocaleAwareApplication() { // no-op, LeakCanary is disabled by default } + // This is for issue https://github.com/mozilla-mobile/fenix/issues/11660. We prefetch our info for startup + // so that we're sure that we have all the data available as our fragment is launched. + private fun prefetchForHomeFragment() { + StrictMode.allowThreadDiskReads().resetPoliciesAfter { + components.core.topSiteStorage.prefetch() + } + } private fun setupPush() { // Sets the PushFeature as the singleton instance for push messages to go to. // We need the push feature setup here to deliver messages in the case where the service diff --git a/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt b/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt index 0922a0606..a9ddaacee 100644 --- a/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt +++ b/app/src/main/java/org/mozilla/fenix/components/TopSiteStorage.kt @@ -14,6 +14,7 @@ import mozilla.components.feature.top.sites.TopSite import mozilla.components.feature.top.sites.TopSiteStorage import mozilla.components.support.locale.LocaleManager import org.mozilla.fenix.R +import org.mozilla.fenix.ext.observeOnce import org.mozilla.fenix.ext.settings import org.mozilla.fenix.settings.SupportUtils import org.mozilla.fenix.settings.advanced.getSelectedLocale @@ -86,4 +87,10 @@ class TopSiteStorage(private val context: Context) { context.settings().defaultTopSitesAdded = true } } + + fun prefetch() { + getTopSites().observeOnce { + cachedTopSites = it + } + } } diff --git a/app/src/main/java/org/mozilla/fenix/ext/LiveData.kt b/app/src/main/java/org/mozilla/fenix/ext/LiveData.kt new file mode 100644 index 000000000..718385219 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/ext/LiveData.kt @@ -0,0 +1,20 @@ +/* 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.ext + +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer + +/** + * Observe a LiveData once and unregister from it as soon as the live data returns a value + */ +fun LiveData.observeOnce(observer: (T) -> Unit) { + observeForever(object : Observer { + override fun onChanged(value: T) { + removeObserver(this) + observer(value) + } + }) +} 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 1ac5e78a2..7fb414bdd 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -215,11 +215,10 @@ class HomeFragment : Fragment() { sessionControlInteractor, homeViewModel ) - activity.themeManager.applyStatusBarTheme(activity) - view.consumeFrom(homeFragmentStore, viewLifecycleOwner) { - sessionControlView?.update(it) - } + updateSessionControlView(view) + + activity.themeManager.applyStatusBarTheme(activity) view.consumeFrom(requireComponents.core.store, viewLifecycleOwner) { val tabCount = if (currentMode.getCurrentMode() == Mode.Normal) { @@ -234,6 +233,20 @@ class HomeFragment : Fragment() { return view } + /** + * The [SessionControlView] is forced to update with our current state when we call + * [HomeFragment.onCreateView] in order to be able to draw everything at once with the current + * data in our store. The [View.consumeFrom] coroutine dispatch + * doesn't get run right away which means that we won't draw on the first layout pass. + */ + fun updateSessionControlView(view: View) { + sessionControlView?.update(homeFragmentStore.state) + + view.consumeFrom(homeFragmentStore, viewLifecycleOwner) { + sessionControlView?.update(it) + } + } + private fun updateLayout(view: View) { val shouldUseBottomToolbar = view.context.settings().shouldUseBottomToolbar diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt index 809b1ec7e..f35b8503f 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt @@ -4,7 +4,6 @@ package org.mozilla.fenix.home.sessioncontrol -import android.os.Build import android.view.View import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager @@ -142,13 +141,8 @@ class SessionControlView( } fun update(state: HomeFragmentState) { - // Workaround for list not updating until scroll on Android 5 + 6 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - sessionControlAdapter.submitList(null) - } val stateAdapterList = state.toAdapterList() - if (homeScreenViewModel.shouldScrollToTopSites) { sessionControlAdapter.submitList(stateAdapterList) {