Merge "Add predictions row to taskbar all apps."
diff --git a/quickstep/res/layout/taskbar_all_apps.xml b/quickstep/res/layout/taskbar_all_apps.xml
index 1ad20cc..11d75c7 100644
--- a/quickstep/res/layout/taskbar_all_apps.xml
+++ b/quickstep/res/layout/taskbar_all_apps.xml
@@ -46,6 +46,8 @@
             android:paddingTop="@dimen/all_apps_header_top_padding"
             android:orientation="vertical">
 
+            <include layout="@layout/floating_header_content" />
+
             <include layout="@layout/all_apps_personal_work_tabs" />
         </com.android.launcher3.allapps.FloatingHeaderView>
 
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 4f987d2..d37e530 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.appprediction;
 
-import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -34,23 +34,17 @@
 import androidx.core.content.ContextCompat;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * A view which shows a horizontal divider
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class AppsDividerView extends View implements StateListener<LauncherState>,
-        FloatingHeaderRow {
-
-    private static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
-    private static final int SHOW_ALL_APPS_LABEL_ON_ALL_APPS_VISITED_COUNT = 20;
+public class AppsDividerView extends View implements FloatingHeaderRow {
 
     public enum DividerType {
         NONE,
@@ -58,7 +52,6 @@
         ALL_APPS_LABEL
     }
 
-    private final Launcher mLauncher;
     private final TextPaint mPaint = new TextPaint();
     private DividerType mDividerType = DividerType.NONE;
 
@@ -86,7 +79,6 @@
 
     public AppsDividerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mLauncher = Launcher.getLauncher(context);
 
         boolean isMainColorDark = Themes.getAttrBoolean(context, R.attr.isMainColorDark);
         mDividerSize = new int[]{
@@ -101,6 +93,9 @@
         mAllAppsLabelTextColor = ContextCompat.getColor(context, isMainColorDark
                 ? R.color.all_apps_label_text_dark
                 : R.color.all_apps_label_text);
+
+        mShowAllAppsLabel = !ActivityContext.lookupContext(
+                getContext()).getOnboardingPrefs().hasReachedMaxCount(ALL_APPS_VISITED_COUNT);
     }
 
     public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
@@ -110,6 +105,14 @@
         updateDividerType();
     }
 
+    /** {@code true} if all apps label should be shown in place of divider. */
+    public void setShowAllAppsLabel(boolean showAllAppsLabel) {
+        if (showAllAppsLabel != mShowAllAppsLabel) {
+            mShowAllAppsLabel = showAllAppsLabel;
+            updateDividerType();
+        }
+    }
+
     @Override
     public int getExpectedHeight() {
         return getPaddingTop() + getPaddingBottom();
@@ -236,51 +239,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        if (shouldShowAllAppsLabel()) {
-            mShowAllAppsLabel = true;
-            mLauncher.getStateManager().addStateListener(this);
-            updateDividerType();
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mLauncher.getStateManager().removeStateListener(this);
-    }
-
-    @Override
-    public void onStateTransitionComplete(LauncherState finalState) {
-        if (finalState == ALL_APPS) {
-            setAllAppsVisitedCount(getAllAppsVisitedCount() + 1);
-        } else {
-            if (mShowAllAppsLabel != shouldShowAllAppsLabel()) {
-                mShowAllAppsLabel = !mShowAllAppsLabel;
-                updateDividerType();
-            }
-
-            if (!mShowAllAppsLabel) {
-                mLauncher.getStateManager().removeStateListener(this);
-            }
-        }
-    }
-
-    private void setAllAppsVisitedCount(int count) {
-        mLauncher.getSharedPrefs().edit().putInt(ALL_APPS_VISITED_COUNT, count).apply();
-    }
-
-    private int getAllAppsVisitedCount() {
-        return mLauncher.getSharedPrefs().getInt(ALL_APPS_VISITED_COUNT, 0);
-    }
-
-    private boolean shouldShowAllAppsLabel() {
-        return getAllAppsVisitedCount() < SHOW_ALL_APPS_LABEL_ON_ALL_APPS_VISITED_COUNT;
-    }
-
-    @Override
     public void setInsets(Rect insets, DeviceProfile grid) {
         int leftRightPadding = grid.allAppsLeftRightPadding;
         setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 9ad8bb2..1dec737 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -31,8 +31,8 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
@@ -43,18 +43,18 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
 @TargetApi(Build.VERSION_CODES.P)
-public class PredictionRowView extends LinearLayout implements
-        OnDeviceProfileChangeListener, FloatingHeaderRow {
+public class PredictionRowView<T extends Context & ActivityContext & DeviceProfileListenable>
+        extends LinearLayout implements OnDeviceProfileChangeListener, FloatingHeaderRow {
 
-    private final Launcher mLauncher;
+    private final T mActivityContext;
     private int mNumPredictedAppsPerRow;
 
     // Helper to drawing the focus indicator.
@@ -64,12 +64,10 @@
     private final List<WorkspaceItemInfo> mPredictedApps = new ArrayList<>();
 
     private FloatingHeaderView mParent;
-    private boolean mScrolledOut;
 
     private boolean mPredictionsEnabled = false;
-
-    @Nullable
-    private List<ItemInfo> mPendingPredictedItems;
+    private @Nullable List<ItemInfo> mPendingPredictedItems;
+    private OnLongClickListener mOnIconLongClickListener = ItemLongClickListener.INSTANCE_ALL_APPS;
 
     public PredictionRowView(@NonNull Context context) {
         this(context, null);
@@ -80,9 +78,9 @@
         setOrientation(LinearLayout.HORIZONTAL);
 
         mFocusHelper = new SimpleFocusIndicatorHelper(this);
-        mLauncher = Launcher.getLauncher(context);
-        mLauncher.addOnDeviceProfileChangeListener(this);
-        mNumPredictedAppsPerRow = mLauncher.getDeviceProfile().numShownAllAppsColumns;
+        mActivityContext = ActivityContext.lookupContext(context);
+        mActivityContext.addOnDeviceProfileChangeListener(this);
+        mNumPredictedAppsPerRow = mActivityContext.getDeviceProfile().numShownAllAppsColumns;
         updateVisibility();
     }
 
@@ -97,11 +95,11 @@
 
     private void updateVisibility() {
         setVisibility(mPredictionsEnabled ? VISIBLE : GONE);
-        if (mLauncher.getAppsView() != null) {
+        if (mActivityContext.getAppsView() != null) {
             if (mPredictionsEnabled) {
-                mLauncher.getAppsView().getAppsStore().registerIconContainer(this);
+                mActivityContext.getAppsView().getAppsStore().registerIconContainer(this);
             } else {
-                mLauncher.getAppsView().getAppsStore().unregisterIconContainer(this);
+                mActivityContext.getAppsView().getAppsStore().unregisterIconContainer(this);
             }
         }
     }
@@ -120,9 +118,9 @@
 
     @Override
     public int getExpectedHeight() {
-        return getVisibility() == GONE ? 0 :
-                Launcher.getLauncher(getContext()).getDeviceProfile().allAppsCellHeightPx
-                        + getPaddingTop() + getPaddingBottom();
+        return getVisibility() == GONE ? 0
+                : mActivityContext.getDeviceProfile().allAppsCellHeightPx + getPaddingTop()
+                        + getPaddingBottom();
     }
 
     @Override
@@ -158,13 +156,12 @@
      */
     public void setPredictedApps(List<ItemInfo> items) {
         if (!FeatureFlags.ENABLE_APP_PREDICTIONS_WHILE_VISIBLE.get()
-                && !mLauncher.isWorkspaceLoading()
+                && !mActivityContext.isBindingItems()
                 && isShown()
                 && getWindowVisibility() == View.VISIBLE) {
             mPendingPredictedItems = items;
             return;
         }
-
         applyPredictedApps(items);
     }
 
@@ -177,6 +174,15 @@
         applyPredictionApps();
     }
 
+    /**
+     * Sets the long click listener for predictions for any future predictions.
+     *
+     * Existing predictions in the container are not updated with this new callback.
+     */
+    public void setOnIconLongClickListener(OnLongClickListener onIconLongClickListener) {
+        mOnIconLongClickListener = onIconLongClickListener;
+    }
+
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         mNumPredictedAppsPerRow = dp.numShownAllAppsColumns;
@@ -189,18 +195,18 @@
             while (getChildCount() > mNumPredictedAppsPerRow) {
                 removeViewAt(0);
             }
-            LayoutInflater inflater = mLauncher.getAppsView().getLayoutInflater();
+            LayoutInflater inflater = mActivityContext.getAppsView().getLayoutInflater();
             while (getChildCount() < mNumPredictedAppsPerRow) {
                 BubbleTextView icon = (BubbleTextView) inflater.inflate(
                         R.layout.all_apps_icon, this, false);
-                icon.setOnClickListener(ItemClickHandler.INSTANCE);
-                icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
+                icon.setOnClickListener(mActivityContext.getItemOnClickListener());
+                icon.setOnLongClickListener(mOnIconLongClickListener);
                 icon.setLongPressTimeoutFactor(1f);
                 icon.setOnFocusChangeListener(mFocusHelper);
 
                 LayoutParams lp = (LayoutParams) icon.getLayoutParams();
                 // Ensure the all apps icon height matches the workspace icons in portrait mode.
-                lp.height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
+                lp.height = mActivityContext.getDeviceProfile().allAppsCellHeightPx;
                 lp.width = 0;
                 lp.weight = 1;
                 addView(icon);
@@ -223,7 +229,6 @@
         boolean predictionsEnabled = predictionCount > 0;
         if (predictionsEnabled != mPredictionsEnabled) {
             mPredictionsEnabled = predictionsEnabled;
-            mLauncher.reapplyUi(false /* cancelCurrentAnimation */);
             updateVisibility();
         }
         mParent.onHeightUpdated();
@@ -237,11 +242,10 @@
 
     @Override
     public void setVerticalScroll(int scroll, boolean isScrolledOut) {
-        mScrolledOut = isScrolledOut;
         if (!isScrolledOut) {
             setTranslationY(scroll);
         }
-        setAlpha(mScrolledOut ? 0 : 1);
+        setAlpha(isScrolledOut ? 0 : 1);
         if (getVisibility() != GONE) {
             AlphaUpdateListener.updateVisibility(this);
         }
@@ -263,6 +267,7 @@
         return getChildAt(0);
     }
 
+
     @Override
     public void onVisibilityAggregated(boolean isVisible) {
         super.onVisibilityAggregated(isVisible);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 78e39bb..fe091ef 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -58,8 +58,11 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
@@ -70,6 +73,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.Themes;
@@ -85,13 +89,16 @@
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
  * that are used by both Launcher and Taskbar (such as Folder) to reference a generic
  * ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer.
  */
-public class TaskbarActivityContext extends ContextThemeWrapper implements ActivityContext {
+public class TaskbarActivityContext extends ContextThemeWrapper implements ActivityContext,
+        DeviceProfileListenable {
 
     private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
             SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
@@ -103,6 +110,7 @@
     private final TaskbarDragLayer mDragLayer;
     private final TaskbarAllAppsContainerView mAppsView;
     private final TaskbarControllers mControllers;
+    private final List<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
 
     private DeviceProfile mDeviceProfile;
 
@@ -124,14 +132,17 @@
     private boolean mIsDestroyed = false;
     // The flag to know if the window is excluded from magnification region computation.
     private boolean mIsExcludeFromMagnificationRegion = false;
+    private boolean mBindingItems = false;
 
     private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
+    private final OnboardingPrefs<TaskbarActivityContext> mOnboardingPrefs;
 
     public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
             TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
             unfoldTransitionProgressProvider) {
         super(windowContext, Themes.getActivityThemeRes(windowContext));
         mDeviceProfile = dp;
+        mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
 
         mNavMode = SysUINavigationMode.getMode(windowContext);
         mImeDrawsImeNavBar = SysUINavigationMode.getImeDrawsImeNavBar(windowContext);
@@ -229,6 +240,7 @@
     public void updateDeviceProfile(DeviceProfile dp) {
         mDeviceProfile = dp;
         updateIconSize(getResources());
+        dispatchDeviceProfileChanged();
     }
 
     private void updateIconSize(Resources resources) {
@@ -305,6 +317,11 @@
     }
 
     @Override
+    public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
+        return mDPChangeListeners;
+    }
+
+    @Override
     public Rect getFolderBoundingBox() {
         return mControllers.taskbarDragLayerController.getFolderBoundingBox();
     }
@@ -394,6 +411,20 @@
         return mAccessibilityDelegate;
     }
 
+    @Override
+    public OnboardingPrefs<TaskbarActivityContext> getOnboardingPrefs() {
+        return mOnboardingPrefs;
+    }
+
+    @Override
+    public boolean isBindingItems() {
+        return mBindingItems;
+    }
+
+    public void setBindingItems(boolean bindingItems) {
+        mBindingItems = bindingItems;
+    }
+
     /**
      * Sets a new data-source for this taskbar instance
      */
@@ -738,6 +769,8 @@
                 "%s\tmIsUserSetupComplete=%b", prefix, mIsUserSetupComplete));
         pw.println(String.format(
                 "%s\tmWindowLayoutParams.height=%dpx", prefix, mWindowLayoutParams.height));
+        pw.println(String.format(
+                "%s\tmBindInProgress=%b", prefix, mBindingItems));
         mControllers.dumpLogs(prefix + "\t", pw);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java
index 62125fe..2670200 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAllAppsViewController.java
@@ -15,8 +15,15 @@
  */
 package com.android.launcher3.taskbar;
 
+import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
+
+import com.android.launcher3.appprediction.AppsDividerView;
+import com.android.launcher3.appprediction.PredictionRowView;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+
+import java.util.List;
 
 /** Handles the {@link TaskbarAllAppsContainerView} initialization and updates. */
 public final class TaskbarAllAppsViewController {
@@ -40,6 +47,9 @@
 
         mAppsView.setOnIconLongClickListener(
                 controllers.taskbarDragController::startDragOnLongClick);
+        mAppsView.getFloatingHeaderView().findFixedRowByType(
+                PredictionRowView.class).setOnIconLongClickListener(
+                controllers.taskbarDragController::startDragOnLongClick);
     }
 
     /** Binds the current {@link AppInfo} instances to the {@link TaskbarAllAppsContainerView}. */
@@ -49,11 +59,26 @@
         }
     }
 
+    /** Binds the current app predictions to all apps {@link PredictionRowView}. */
+    public void setPredictedApps(List<ItemInfo> predictedApps) {
+        if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
+            PredictionRowView<?> predictionRowView =
+                    mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class);
+            predictionRowView.setPredictedApps(predictedApps);
+        }
+    }
+
     /** Opens the {@link TaskbarAllAppsContainerView}. */
     public void show() {
-        if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
-            mContext.setTaskbarWindowFullscreen(true);
-            mSlideInView.show();
+        if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
+            return;
         }
+
+        mAppsView.getFloatingHeaderView().findFixedRowByType(AppsDividerView.class)
+                .setShowAllAppsLabel(
+                        !mContext.getOnboardingPrefs().hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
+        mContext.getOnboardingPrefs().incrementEventCount(ALL_APPS_VISITED_COUNT);
+        mContext.setTaskbarWindowFullscreen(true);
+        mSlideInView.show();
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 2bafccd..2e18a40 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -52,8 +52,6 @@
     // Initialized in init.
     private TaskbarControllers mControllers;
 
-    private boolean mBindInProgress = false;
-
     public TaskbarModelCallbacks(
             TaskbarActivityContext context, TaskbarView container) {
         mContext = context;
@@ -66,14 +64,14 @@
 
     @Override
     public void startBinding() {
-        mBindInProgress = true;
+        mContext.setBindingItems(true);
         mHotseatItems.clear();
         mPredictedItems = Collections.emptyList();
     }
 
     @Override
     public void finishBindingItems(IntSet pagesBoundFirst) {
-        mBindInProgress = false;
+        mContext.setBindingItems(false);
         commitItemsToUI();
     }
 
@@ -159,11 +157,13 @@
         if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
             mPredictedItems = item.items;
             commitItemsToUI();
+        } else if (item.containerId == Favorites.CONTAINER_PREDICTION) {
+            mControllers.taskbarAllAppsViewController.setPredictedApps(item.items);
         }
     }
 
     private void commitItemsToUI() {
-        if (mBindInProgress) {
+        if (mContext.isBindingItems()) {
             return;
         }
 
@@ -212,6 +212,5 @@
             pw.println(
                     String.format("%s\tpredicted items count=%s", prefix, mPredictedItems.size()));
         }
-        pw.println(String.format("%s\tmBindInProgress=%b", prefix, mBindInProgress));
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 9050ddc..1073d76 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -69,7 +69,6 @@
 import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
 import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.UiThreadHelper;
@@ -162,7 +161,7 @@
     }
 
     @Override
-    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
+    protected QuickstepOnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
         return new QuickstepOnboardingPrefs(this, sharedPrefs);
     }
 
@@ -235,8 +234,10 @@
     public void bindExtraContainerItems(FixedContainerItems item) {
         if (item.containerId == Favorites.CONTAINER_PREDICTION) {
             mAllAppsPredictions = item;
-            getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
-                    .setPredictedApps(item.items);
+            PredictionRowView<?> predictionRowView =
+                    getAppsView().getFloatingHeaderView().findFixedRowByType(
+                            PredictionRowView.class);
+            predictionRowView.setPredictedApps(item.items);
         } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
             mHotseatPredictionController.setPredictedItems(item);
         } else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) {
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 6d6e802..54642a2 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -28,6 +28,7 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.appprediction.AppsDividerView;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.statemanager.StateManager;
@@ -132,5 +133,24 @@
                 }
             });
         }
+
+        if (!hasReachedMaxCount(ALL_APPS_VISITED_COUNT)) {
+            mLauncher.getStateManager().addStateListener(new StateListener<LauncherState>() {
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    if (finalState == ALL_APPS) {
+                        incrementEventCount(ALL_APPS_VISITED_COUNT);
+                        return;
+                    }
+
+                    boolean hasReachedMaxCount = hasReachedMaxCount(ALL_APPS_VISITED_COUNT);
+                    mLauncher.getAppsView().getFloatingHeaderView().findFixedRowByType(
+                            AppsDividerView.class).setShowAllAppsLabel(!hasReachedMaxCount);
+                    if (hasReachedMaxCount) {
+                        mLauncher.getStateManager().removeStateListener(this);
+                    }
+                }
+            });
+        }
     }
 }
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index ec96c6d..42f7053 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -34,6 +34,7 @@
 
 import androidx.annotation.IntDef;
 
+import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.util.SystemUiController;
@@ -44,11 +45,13 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Launcher BaseActivity
  */
-public abstract class BaseActivity extends Activity implements ActivityContext {
+public abstract class BaseActivity extends Activity implements ActivityContext,
+        DeviceProfileListenable {
 
     private static final String TAG = "BaseActivity";
 
@@ -142,6 +145,11 @@
         return mDeviceProfile;
     }
 
+    @Override
+    public List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners() {
+        return mDPChangeListeners;
+    }
+
     /**
      * Returns {@link StatsLogManager} for user event logging.
      */
@@ -261,20 +269,6 @@
 
     protected void onActivityFlagsChanged(int changeBits) { }
 
-    public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
-        mDPChangeListeners.add(listener);
-    }
-
-    public void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
-        mDPChangeListeners.remove(listener);
-    }
-
-    protected void dispatchDeviceProfileChanged() {
-        for (int i = mDPChangeListeners.size() - 1; i >= 0; i--) {
-            mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile);
-        }
-    }
-
     public void addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) {
         mMultiWindowModeChangedListeners.add(listener);
     }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c456a4d..62703ad 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.util.WindowBounds;
 
 import java.io.PrintWriter;
+import java.util.List;
 
 @SuppressLint("NewApi")
 public class DeviceProfile {
@@ -1223,6 +1224,35 @@
         void onDeviceProfileChanged(DeviceProfile dp);
     }
 
+    /** Allows registering listeners for {@link DeviceProfile} changes. */
+    public interface DeviceProfileListenable {
+
+        /** The current device profile. */
+        DeviceProfile getDeviceProfile();
+
+        /** Registered {@link OnDeviceProfileChangeListener} instances. */
+        List<OnDeviceProfileChangeListener> getOnDeviceProfileChangeListeners();
+
+        /** Notifies listeners of a {@link DeviceProfile} change. */
+        default void dispatchDeviceProfileChanged() {
+            DeviceProfile deviceProfile = getDeviceProfile();
+            List<OnDeviceProfileChangeListener> listeners = getOnDeviceProfileChangeListeners();
+            for (int i = listeners.size() - 1; i >= 0; i--) {
+                listeners.get(i).onDeviceProfileChanged(deviceProfile);
+            }
+        }
+
+        /** Register listener for {@link DeviceProfile} changes. */
+        default void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
+            getOnDeviceProfileChangeListeners().add(listener);
+        }
+
+        /** Unregister listener for {@link DeviceProfile} changes. */
+        default void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
+            getOnDeviceProfileChangeListeners().remove(listener);
+        }
+    }
+
     public static class Builder {
         private Context mContext;
         private InvariantDeviceProfile mInv;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ebc0c75..fcd147f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -351,7 +351,7 @@
     // We only want to get the SharedPreferences once since it does an FS stat each time we get
     // it from the context.
     private SharedPreferences mSharedPrefs;
-    private OnboardingPrefs mOnboardingPrefs;
+    private OnboardingPrefs<? extends Launcher> mOnboardingPrefs;
 
     // Activity result which needs to be processed after workspace has loaded.
     private ActivityResultInfo mPendingActivityResult;
@@ -557,11 +557,12 @@
         return new LauncherOverlayManager() { };
     }
 
-    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
+    protected OnboardingPrefs<? extends Launcher> createOnboardingPrefs(
+            SharedPreferences sharedPrefs) {
         return new OnboardingPrefs<>(this, sharedPrefs);
     }
 
-    public OnboardingPrefs getOnboardingPrefs() {
+    public OnboardingPrefs<? extends Launcher> getOnboardingPrefs() {
         return mOnboardingPrefs;
     }
 
@@ -588,7 +589,7 @@
     }
 
     @Override
-    protected void dispatchDeviceProfileChanged() {
+    public void dispatchDeviceProfileChanged() {
         super.dispatchDeviceProfileChanged();
         mOverlayManager.onDeviceProvideChanged();
     }
@@ -1782,6 +1783,11 @@
         return mWorkspaceLoading;
     }
 
+    @Override
+    public boolean isBindingItems() {
+        return mWorkspaceLoading;
+    }
+
     private void setWorkspaceLoading(boolean value) {
         mWorkspaceLoading = value;
     }
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index fb87f88..114f813 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -62,7 +62,6 @@
 
     public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mActivityContext.addOnDeviceProfileChangeListener(this);
     }
 
     public SearchUiManager getSearchUiManager() {
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index a66ae78..59e21c0 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -48,6 +48,7 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
@@ -75,9 +76,10 @@
  *
  * @param <T> Type of context inflating all apps.
  */
-public abstract class BaseAllAppsContainerView<T extends Context & ActivityContext> extends
-        SpringRelativeLayout implements DragSource, Insettable, OnDeviceProfileChangeListener,
-        OnActivePageChangedListener, ScrimView.ScrimDrawingController {
+public abstract class BaseAllAppsContainerView<T extends Context & ActivityContext
+        & DeviceProfileListenable> extends SpringRelativeLayout implements DragSource, Insettable,
+        OnDeviceProfileChangeListener, OnActivePageChangedListener,
+        ScrimView.ScrimDrawingController {
 
     private static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
 
@@ -148,6 +150,7 @@
         mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
 
         mAllAppsStore.addUpdateListener(this::onAppsUpdated);
+        mActivityContext.addOnDeviceProfileChangeListener(this);
     }
 
     /** Creates the adapter provider for the main section. */
@@ -580,7 +583,10 @@
 
     void setupHeader() {
         mHeader.setVisibility(View.VISIBLE);
-        mHeader.setup(mAH, mAH.get(AdapterHolder.WORK).mRecyclerView == null);
+        mHeader.setup(
+                mAH.get(AdapterHolder.MAIN).mRecyclerView,
+                mAH.get(AdapterHolder.WORK).mRecyclerView,
+                mAH.get(AdapterHolder.WORK).mRecyclerView == null);
 
         int padding = mHeader.getMaxTranslation();
         for (int i = 0; i < mAH.size(); i++) {
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 059d4b8..ed8761e 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -41,7 +41,6 @@
 import com.android.systemui.plugins.PluginListener;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
 
 public class FloatingHeaderView extends LinearLayout implements
@@ -226,8 +225,7 @@
         return super.getFocusedChild();
     }
 
-    <T extends Context & ActivityContext> void setup(
-            List<BaseAllAppsContainerView<T>.AdapterHolder> mAH, boolean tabsHidden) {
+    void setup(AllAppsRecyclerView mainRV, AllAppsRecyclerView workRV, boolean tabsHidden) {
         for (FloatingHeaderRow row : mAllRows) {
             row.setup(this, mAllRows, tabsHidden);
         }
@@ -235,10 +233,8 @@
 
         mTabsHidden = tabsHidden;
         mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
-        mMainRV = setupRV(mMainRV,
-                mAH.get(BaseAllAppsContainerView.AdapterHolder.MAIN).mRecyclerView);
-        mWorkRV = setupRV(mWorkRV,
-                mAH.get(BaseAllAppsContainerView.AdapterHolder.WORK).mRecyclerView);
+        mMainRV = setupRV(mMainRV, mainRV);
+        mWorkRV = setupRV(mWorkRV, workRV);
         mParent = (ViewGroup) mMainRV.getParent();
         setMainActive(mMainRVActive || mWorkRV == null);
         reset(false);
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 5ba0d30..39d7cfe 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -20,7 +20,7 @@
 
 import androidx.annotation.StringDef;
 
-import com.android.launcher3.Launcher;
+import com.android.launcher3.views.ActivityContext;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -29,8 +29,10 @@
 
 /**
  * Stores and retrieves onboarding-related data via SharedPreferences.
+ *
+ * @param <T> Context which owns these preferences.
  */
-public class OnboardingPrefs<T extends Launcher> {
+public class OnboardingPrefs<T extends ActivityContext> {
 
     public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
     public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
@@ -39,13 +41,15 @@
     public static final String SEARCH_EDU_SEEN = "launcher.search_edu_seen";
     public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count";
     public static final String TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen";
+    public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
     // When adding a new key, add it here as well, to be able to reset it from Developer Options.
     public static final Map<String, String[]> ALL_PREF_KEYS = Map.of(
             "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT },
             "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT,
                     HOTSEAT_LONGPRESS_TIP_SEEN },
             "Search Education", new String[] { SEARCH_EDU_SEEN, SEARCH_SNACKBAR_COUNT },
-            "Taskbar Education", new String[] { TASKBAR_EDU_SEEN }
+            "Taskbar Education", new String[] { TASKBAR_EDU_SEEN },
+            "All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT}
     );
 
     /**
@@ -67,7 +71,8 @@
     @StringDef(value = {
             HOME_BOUNCE_COUNT,
             HOTSEAT_DISCOVERY_TIP_COUNT,
-            SEARCH_SNACKBAR_COUNT
+            SEARCH_SNACKBAR_COUNT,
+            ALL_APPS_VISITED_COUNT
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventCountKey {
@@ -80,6 +85,7 @@
         maxCounts.put(HOME_BOUNCE_COUNT, 3);
         maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
         maxCounts.put(SEARCH_SNACKBAR_COUNT, 3);
+        maxCounts.put(ALL_APPS_VISITED_COUNT, 20);
         MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
     }
 
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index a318363..3c90eea 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.model.StringCache;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.ViewCache;
 
 /**
@@ -138,6 +139,16 @@
      */
     default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) { }
 
+    /** Onboarding preferences for any onboarding data within this context. */
+    default OnboardingPrefs<?> getOnboardingPrefs() {
+        return null;
+    }
+
+    /** Returns {@code true} if items are currently being bound within this context. */
+    default boolean isBindingItems() {
+        return false;
+    }
+
     /**
      * Returns the ActivityContext associated with the given Context, or throws an exception if
      * the Context is not associated with any ActivityContext.