diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index 6ad5221749..82ed9621dd 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -18,6 +18,7 @@ package com.android.launcher3.allapps; import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH; import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD; import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD; +import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY; import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_RV_PREINFLATION; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB; @@ -552,17 +553,14 @@ public class ActivityAllAppsContainerView mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView); mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView); + final AllAppsRecyclerView mainRecyclerView; + final AllAppsRecyclerView workRecyclerView; if (mUsingTabs) { - mAH.get(AdapterHolder.MAIN).setup(mViewPager.getChildAt(0), mPersonalMatcher); - mAH.get(AdapterHolder.WORK).setup(mViewPager.getChildAt(1), mWorkManager.getMatcher()); - mAH.get(AdapterHolder.WORK).mRecyclerView.setId(R.id.apps_list_view_work); - if (ENABLE_ALL_APPS_RV_PREINFLATION.get()) { - // Let main and work rv share same view pool. - ((RecyclerView) mViewPager.getChildAt(0)) - .setRecycledViewPool(mAllAppsStore.getRecyclerViewPool()); - ((RecyclerView) mViewPager.getChildAt(1)) - .setRecycledViewPool(mAllAppsStore.getRecyclerViewPool()); - } + mainRecyclerView = (AllAppsRecyclerView) mViewPager.getChildAt(0); + workRecyclerView = (AllAppsRecyclerView) mViewPager.getChildAt(1); + mAH.get(AdapterHolder.MAIN).setup(mainRecyclerView, mPersonalMatcher); + mAH.get(AdapterHolder.WORK).setup(workRecyclerView, mWorkManager.getMatcher()); + workRecyclerView.setId(R.id.apps_list_view_work); if (FeatureFlags.ENABLE_EXPANDING_PAUSE_WORK_BUTTON.get()) { mAH.get(AdapterHolder.WORK).mRecyclerView.addOnScrollListener( mWorkManager.newScrollListener()); @@ -587,13 +585,15 @@ public class ActivityAllAppsContainerView onActivePageChanged(mViewPager.getNextPage()); } } else { - mAH.get(AdapterHolder.MAIN).setup(findViewById(R.id.apps_list_view), null); + mainRecyclerView = findViewById(R.id.apps_list_view); + workRecyclerView = null; + mAH.get(AdapterHolder.MAIN).setup(mainRecyclerView, null); mAH.get(AdapterHolder.WORK).mRecyclerView = null; - if (ENABLE_ALL_APPS_RV_PREINFLATION.get()) { - mAH.get(AdapterHolder.MAIN).mRecyclerView - .setRecycledViewPool(mAllAppsStore.getRecyclerViewPool()); - } } + setUpCustomRecyclerViewPool( + mainRecyclerView, + workRecyclerView, + mAllAppsStore.getRecyclerViewPool()); setupHeader(); if (isSearchBarFloating()) { @@ -610,6 +610,30 @@ public class ActivityAllAppsContainerView mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView); } + /** + * If {@link ENABLE_ALL_APPS_RV_PREINFLATION} is enabled, wire custom + * {@link RecyclerView.RecycledViewPool} to main and work {@link AllAppsRecyclerView}. + * + * Then if {@link ALL_APPS_GONE_VISIBILITY} is enabled, update max pool size. This is because + * all apps rv's hidden visibility is changed to {@link View#GONE} from {@link View#INVISIBLE), + * thus we cannot rely on layout pass to update pool size. + */ + private static void setUpCustomRecyclerViewPool( + @NonNull AllAppsRecyclerView mainRecyclerView, + @Nullable AllAppsRecyclerView workRecyclerView, + @NonNull RecyclerView.RecycledViewPool recycledViewPool) { + if (!ENABLE_ALL_APPS_RV_PREINFLATION.get()) { + return; + } + mainRecyclerView.setRecycledViewPool(recycledViewPool); + if (workRecyclerView != null) { + workRecyclerView.setRecycledViewPool(recycledViewPool); + } + if (ALL_APPS_GONE_VISIBILITY.get()) { + mainRecyclerView.updatePoolSize(); + } + } + private void replaceAppsRVContainer(boolean showTabs) { for (int i = AdapterHolder.MAIN; i <= AdapterHolder.WORK; i++) { AdapterHolder adapterHolder = mAH.get(i); diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 602d1a38a5..7edbeac71f 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo; import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN; @@ -26,6 +27,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_COLLAPSE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_EXTEND; +import static com.android.launcher3.recyclerview.AllAppsRecyclerViewPoolKt.EXTRA_ICONS_COUNT; +import static com.android.launcher3.recyclerview.AllAppsRecyclerViewPoolKt.PREINFLATE_ICONS_ROW_COUNT; import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING; import android.content.Context; @@ -96,8 +99,18 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1); + + // If all apps' hidden visibility is INVISIBLE, we will need to preinflate one page of + // all apps icons for smooth scrolling. + int maxPoolSizeForAppIcons = (approxRows + 1) * grid.numShownAllAppsColumns; + if (ALL_APPS_GONE_VISIBILITY.get()) { + // If all apps' hidden visibility is GONE, we need to increase prefinated icons number + // by [PREINFLATE_ICONS_ROW_COUNT] rows + [EXTRA_ICONS_COUNT] for fast opening all apps. + maxPoolSizeForAppIcons += + PREINFLATE_ICONS_ROW_COUNT * grid.numShownAllAppsColumns + EXTRA_ICONS_COUNT; + } pool.setMaxRecycledViews( - AllAppsGridAdapter.VIEW_TYPE_ICON, (approxRows + 1) * grid.numShownAllAppsColumns); + AllAppsGridAdapter.VIEW_TYPE_ICON, maxPoolSizeForAppIcons); } @Override diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 0d7b736cc8..c09a5b9d93 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -438,7 +438,8 @@ public class AllAppsTransitionController mAppsView = appsView; mAppsView.setScrimView(scrimView); - mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT); + mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT, + FeatureFlags.ALL_APPS_GONE_VISIBILITY.get() ? View.GONE : View.INVISIBLE); mAppsViewAlpha.setUpdateVisibility(true); mAppsViewTranslationY = new MultiPropertyFactory<>( mAppsView, VIEW_TRANSLATE_Y, APPS_VIEW_INDEX_COUNT, Float::sum); diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java index 8dad1b4fa5..4382174530 100644 --- a/src/com/android/launcher3/anim/AlphaUpdateListener.java +++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java @@ -53,8 +53,18 @@ public class AlphaUpdateListener extends AnimatorListenerAdapter } public static void updateVisibility(View view) { - if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != View.INVISIBLE) { - view.setVisibility(View.INVISIBLE); + updateVisibility(view, View.INVISIBLE); + } + + /** + * Update view's visibility. + * + * @param view View that needs to update visibility. + * @param hiddenVisibility {@link View#GONE} or {@link View#INVISIBLE} + */ + public static void updateVisibility(View view, int hiddenVisibility) { + if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != hiddenVisibility) { + view.setVisibility(hiddenVisibility); } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != View.VISIBLE) { if (view instanceof ViewGroup) { diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 0ee4414334..cabcabcb28 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -395,10 +395,15 @@ public final class FeatureFlags { // TODO(Block 33): Clean up flags public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355, - "ENABLE_ALL_APPS_RV_PREINFLATION", DISABLED, + "ENABLE_ALL_APPS_RV_PREINFLATION", ENABLED, "Enables preinflating all apps icons to avoid scrolling jank."); - // TODO(Block 34): Empty block + // TODO(Block 34): Clean up flags + public static final BooleanFlag ALL_APPS_GONE_VISIBILITY = getDebugFlag(291651514, + "ALL_APPS_GONE_VISIBILITY", ENABLED, + "Set all apps container view's hidden visibility to GONE instead of INVISIBLE."); + + // TODO(Block 35): Empty block public static class BooleanFlag { diff --git a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt index 26dde29d36..3c59c1d63f 100644 --- a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt +++ b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt @@ -22,13 +22,14 @@ import androidx.recyclerview.widget.RecyclerView.RecycledViewPool import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.android.launcher3.BubbleTextView import com.android.launcher3.allapps.BaseAllAppsAdapter +import com.android.launcher3.config.FeatureFlags import com.android.launcher3.util.Executors.MAIN_EXECUTOR import com.android.launcher3.util.Executors.VIEW_PREINFLATION_EXECUTOR import com.android.launcher3.views.ActivityContext import java.util.concurrent.Future -private const val PREINFLATE_ICONS_ROW_COUNT = 4 -private const val EXTRA_ICONS_COUNT = 2 +const val PREINFLATE_ICONS_ROW_COUNT = 4 +const val EXTRA_ICONS_COUNT = 2 /** * An [RecycledViewPool] that preinflates app icons ([ViewHolder] of [BubbleTextView]) of all apps @@ -81,11 +82,21 @@ class AllAppsRecyclerViewPool : RecycledViewPool() { * After testing on phone, foldable and tablet, we found [PREINFLATE_ICONS_ROW_COUNT] rows of * app icons plus [EXTRA_ICONS_COUNT] is the magic minimal count of app icons to preinflate to * suffice fast scrolling. + * + * Note that if [FeatureFlags.ALL_APPS_GONE_VISIBILITY] is enabled, we need to preinfate extra + * app icons in size of one all apps pages, so that opening all apps don't need to inflate app + * icons. */ fun getPreinflateCount(context: T): Int where T : Context, T : ActivityContext { - val targetPreinflateCount = + var targetPreinflateCount = PREINFLATE_ICONS_ROW_COUNT * context.deviceProfile.numShownAllAppsColumns + EXTRA_ICONS_COUNT + if (FeatureFlags.ALL_APPS_GONE_VISIBILITY.get()) { + val grid = ActivityContext.lookupContext(context).deviceProfile + val approxRows = + Math.ceil((grid.availableHeightPx / grid.allAppsIconSizePx).toDouble()).toInt() + targetPreinflateCount += (approxRows + 1) * grid.numShownAllAppsColumns + } val existingPreinflateCount = getRecycledViewCount(BaseAllAppsAdapter.VIEW_TYPE_ICON) return targetPreinflateCount - existingPreinflateCount } diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java index ac016a8595..a66a9d2d56 100644 --- a/src/com/android/launcher3/util/MultiValueAlpha.java +++ b/src/com/android/launcher3/util/MultiValueAlpha.java @@ -32,8 +32,15 @@ public class MultiValueAlpha extends MultiPropertyFactory { // Whether we should change from INVISIBLE to VISIBLE and vice versa at low alpha values. private boolean mUpdateVisibility; + private final int mHiddenVisibility; + public MultiValueAlpha(View view, int size) { + this(view, size, View.INVISIBLE); + } + + public MultiValueAlpha(View view, int size, int hiddenVisibility) { super(view, VIEW_ALPHA, size, ALPHA_AGGREGATOR, 1f); + this.mHiddenVisibility = hiddenVisibility; } /** Sets whether we should update between INVISIBLE and VISIBLE based on alpha. */ @@ -45,7 +52,7 @@ public class MultiValueAlpha extends MultiPropertyFactory { protected void apply(float value) { super.apply(value); if (mUpdateVisibility) { - AlphaUpdateListener.updateVisibility(mTarget); + AlphaUpdateListener.updateVisibility(mTarget, mHiddenVisibility); } } }