Merge "[Predictive Back] Gate feature under experiment flag" into udc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index b1064f7..bf84820 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -177,7 +177,7 @@
   ALL_APPS_SEARCH_RESULT_SUGGEST = 22 [deprecated = true];
   ALL_APPS_SEARCH_RESULT_ASSISTANT = 23;
   ALL_APPS_SEARCH_RESULT_CHROMETAB = 24;
-  ALL_APPS_SEARCH_RESULT_NAVVYSITE = 25;
+  ALL_APPS_SEARCH_RESULT_NAVVYSITE = 25 [deprecated = true];
   ALL_APPS_SEARCH_RESULT_TIPS = 26;
   ALL_APPS_SEARCH_RESULT_PEOPLE_TILE = 27;
   ALL_APPS_SEARCH_RESULT_LEGACY_SHORTCUT = 30;
diff --git a/quickstep/res/drawable/ic_save_app_pair.xml b/quickstep/res/drawable/ic_save_app_pair.xml
new file mode 100644
index 0000000..4a7ee1a
--- /dev/null
+++ b/quickstep/res/drawable/ic_save_app_pair.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+     Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M13.329,2.305H4.242C2.751,2.305 1.542,3.514 1.542,5.005V13.005C1.542,14.496 2.751,15.705 4.242,15.705H7.875V19.011C7.875,20.502 9.084,21.711 10.575,21.711H19.662C21.153,21.711 22.362,20.502 22.362,19.011V10.011C22.362,8.52 21.153,7.311 19.662,7.311H16.029V5.005C16.029,3.514 14.821,2.305 13.329,2.305ZM14.329,7.311V5.005C14.329,4.452 13.882,4.005 13.329,4.005H4.242C3.69,4.005 3.242,4.452 3.242,5.005V13.005C3.242,13.557 3.69,14.005 4.242,14.005H7.875V10.011C7.875,8.52 9.084,7.311 10.575,7.311H14.329ZM9.575,14.005V10.011C9.575,9.611 9.81,9.266 10.15,9.106C10.285,9.037 10.438,8.999 10.6,8.999H19.687C20.239,8.999 20.687,9.447 20.687,9.999V18.999C20.687,19.399 20.452,19.744 20.113,19.904C19.977,19.972 19.824,20.011 19.662,20.011H10.575C10.023,20.011 9.575,19.563 9.575,19.011V15.705H9.6V14.005H9.575ZM15.542,11.996V14H17.588V15H15.542V16.996H14.542V15H12.464V14H14.542V11.996H15.542Z"
+      android:fillColor="#000000"
+      android:fillType="evenOdd"/>
+</vector>
diff --git a/quickstep/res/layout/taskbar_edu.xml b/quickstep/res/layout/taskbar_edu.xml
index d7daea3..f3856ba 100644
--- a/quickstep/res/layout/taskbar_edu.xml
+++ b/quickstep/res/layout/taskbar_edu.xml
@@ -55,7 +55,7 @@
             style="@style/TaskbarEdu.Button.Close"
             android:textColor="?android:attr/textColorPrimary"/>
 
-        <com.android.launcher3.pageindicators.PageIndicatorDots
+        <com.android.launcher3.pageindicators.LauncherDotsPageIndicator
             android:id="@+id/content_page_indicator"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -63,6 +63,7 @@
             app:layout_constraintBottom_toBottomOf="@id/edu_start_button"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
+            app:indicatorDotColor="@color/folder_pagination_color"
             android:elevation="1dp" />
 
         <Button
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 8baa324..7eecb29 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -321,7 +321,7 @@
         ActivityOptions options = ActivityOptions.makeRemoteAnimation(
                 new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay),
                 new RemoteTransition(runner.toRemoteTransition(),
-                        mLauncher.getIApplicationThread()));
+                        mLauncher.getIApplicationThread(), "QuickstepLaunch"));
         return new ActivityOptionsWrapper(options, onEndCallback);
     }
 
@@ -1122,7 +1122,7 @@
             mLauncherOpenTransition = new RemoteTransition(
                     new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
                             false /* startAtFrontOfQueue */).toRemoteTransition(),
-                    mLauncher.getIApplicationThread());
+                    mLauncher.getIApplicationThread(), "QuickstepLaunchHome");
 
             TransitionFilter homeCheck = new TransitionFilter();
             // No need to handle the transition that also dismisses keyguard.
@@ -1472,13 +1472,13 @@
                     RemoteAnimationTarget target = appTargets[i];
                     SurfaceProperties builder = transaction.forSurface(target.leash);
 
-                    if (target.localBounds != null) {
-                        tmpPos.set(target.localBounds.left, target.localBounds.top);
+                    if (target.screenSpaceBounds != null) {
+                        tmpPos.set(target.screenSpaceBounds.left, target.screenSpaceBounds.top);
                     } else {
                         tmpPos.set(target.position.x, target.position.y);
                     }
 
-                    final Rect crop = new Rect(target.screenSpaceBounds);
+                    final Rect crop = new Rect(target.localBounds);
                     crop.offsetTo(0, 0);
                     if (target.mode == MODE_CLOSING) {
                         tmpRect.set(target.screenSpaceBounds);
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index e8f2496..3510fbe 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -76,7 +76,6 @@
 
         mFocusHelper = new SimpleFocusIndicatorHelper(this);
         mActivityContext = ActivityContext.lookupContext(context);
-        mActivityContext.addOnDeviceProfileChangeListener(this);
         mNumPredictedAppsPerRow = mActivityContext.getDeviceProfile().numShownAllAppsColumns;
         updateVisibility();
     }
@@ -84,6 +83,13 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
+        mActivityContext.addOnDeviceProfileChangeListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mActivityContext.removeOnDeviceProfileChangeListener(this);
     }
 
     public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
index 6cd6512..d0e6386 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
@@ -25,12 +25,12 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
-import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.pageindicators.LauncherDotsPageIndicator;
 import com.android.launcher3.taskbar.TaskbarEduController.TaskbarEduCallbacks;
 import com.android.launcher3.views.ActivityContext;
 
 /** Horizontal carousel of tutorial screens for Taskbar Edu. */
-public class TaskbarEduPagedView extends PagedView<PageIndicatorDots> {
+public class TaskbarEduPagedView extends PagedView<LauncherDotsPageIndicator> {
 
     private TaskbarEduView mTaskbarEduView;
     private TaskbarEduCallbacks mControllerCallbacks;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 1adbf39..5fedb3d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -70,20 +70,9 @@
     fun init(controllers: TaskbarControllers) {
         this.controllers = controllers
         windowLayoutParams = context.windowLayoutParams
-        windowLayoutParams.providedInsets =
-            arrayOf(
-                InsetsFrameProvider(insetsOwner, 0, navigationBars()),
-                InsetsFrameProvider(insetsOwner, 0, tappableElement()),
-                InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
-                InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
-                    .setSource(SOURCE_DISPLAY),
-                InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures())
-                    .setSource(SOURCE_DISPLAY)
-            )
-
+        windowLayoutParams.insetsRoundedCornerFrame = true
         onTaskbarWindowHeightOrInsetsChanged()
 
-        windowLayoutParams.insetsRoundedCornerFrame = true
         context.addOnDeviceProfileChangeListener(deviceProfileChangeListener)
         gestureNavSettingsObserver.registerForCallingUser()
     }
@@ -94,6 +83,25 @@
     }
 
     fun onTaskbarWindowHeightOrInsetsChanged() {
+        if (context.isGestureNav) {
+            windowLayoutParams.providedInsets =
+                    arrayOf(
+                            InsetsFrameProvider(insetsOwner, 0, navigationBars()),
+                            InsetsFrameProvider(insetsOwner, 0, tappableElement()),
+                            InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
+                            InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
+                                    .setSource(SOURCE_DISPLAY),
+                            InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures())
+                                    .setSource(SOURCE_DISPLAY)
+                    )
+        } else {
+            windowLayoutParams.providedInsets =
+                    arrayOf(
+                            InsetsFrameProvider(insetsOwner, 0, navigationBars()),
+                            InsetsFrameProvider(insetsOwner, 0, tappableElement())
+                    )
+        }
+
         val touchableHeight = controllers.taskbarStashController.touchableHeight
         touchableRegion.set(
             0,
@@ -146,7 +154,8 @@
         for (provider in windowLayoutParams.providedInsets) {
             if (context.isGestureNav && provider.type == tappableElement()) {
                 provider.insetsSizeOverrides = insetsSizeOverrideForGestureNavTappableElement
-            } else {
+            } else if (provider.type != systemGestures()) {
+                // We only override insets at the bottom of the screen
                 provider.insetsSizeOverrides = insetsSizeOverride
             }
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 9db4ddd..d69769a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -112,13 +112,24 @@
         DeviceProfile dp = mActivityContext.getDeviceProfile();
         setShiftRange(dp.allAppsShiftRange);
 
-        mActivityContext.addOnDeviceProfileChangeListener(this);
         setContentBackgroundWithParent(
                 getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet),
                 mAppsView.getBottomSheetBackground());
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mActivityContext.addOnDeviceProfileChangeListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mActivityContext.removeOnDeviceProfileChangeListener(this);
+    }
+
+    @Override
     protected void onScaleProgressChanged() {
         super.onScaleProgressChanged();
         mAppsView.setClipChildren(!mIsBackProgressing);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index dfac37f..cc56263 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -641,7 +641,7 @@
         PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration());
         RectF startingTaskRect = new RectF();
         final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(this,
-                source.view, null /* thumbnail */, source.drawable, startingTaskRect);
+                source.getView(), null /* thumbnail */, source.getDrawable(), startingTaskRect);
         floatingTaskView.setAlpha(1);
         floatingTaskView.addStagingAnimation(anim, startingTaskRect, tempRect,
                 false /* fadeWithThumbnail */, true /* isStagedTask */);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DebugFlag.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DebugFlag.java
index d4944d0..481e200 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DebugFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DebugFlag.java
@@ -15,16 +15,20 @@
  */
 package com.android.launcher3.uioverrides.flags;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.config.FeatureFlags.BooleanFlag;
+import com.android.launcher3.config.FeatureFlags.FlagState;
 
 class DebugFlag extends BooleanFlag {
 
     public final String key;
     public final String description;
 
-    public final boolean defaultValue;
+    @NonNull
+    public final FlagState defaultValue;
 
-    public DebugFlag(String key, String description, boolean defaultValue, boolean currentValue) {
+    DebugFlag(String key, String description, FlagState defaultValue, boolean currentValue) {
         super(currentValue);
         this.key = key;
         this.defaultValue = defaultValue;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
index 67ea1af..89aba90 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
@@ -39,6 +39,7 @@
 import android.os.Bundle;
 import android.provider.Settings;
 import android.text.Editable;
+import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.ArrayMap;
 import android.util.Pair;
@@ -182,9 +183,16 @@
     }
 
     private PreferenceCategory newCategory(String title) {
+        return newCategory(title, null);
+    }
+
+    private PreferenceCategory newCategory(String title, @Nullable String summary) {
         PreferenceCategory category = new PreferenceCategory(getContext());
         category.setOrder(Preference.DEFAULT_ORDER);
         category.setTitle(title);
+        if (!TextUtils.isEmpty(summary)) {
+            category.setSummary(summary);
+        }
         mPreferenceScreen.addPreference(category);
         return category;
     }
@@ -195,7 +203,7 @@
         }
 
         mFlagTogglerPrefUi = new FlagTogglerPrefUi(this);
-        mFlagTogglerPrefUi.applyTo(newCategory("Feature flags"));
+        mFlagTogglerPrefUi.applyTo(newCategory("Feature flags", "Long press to reset"));
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeviceFlag.java
index 3900ebb..035beb4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeviceFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeviceFlag.java
@@ -16,11 +16,13 @@
 
 package com.android.launcher3.uioverrides.flags;
 
+import com.android.launcher3.config.FeatureFlags.FlagState;
+
 class DeviceFlag extends DebugFlag {
 
     private final boolean mDefaultValueInCode;
 
-    public DeviceFlag(String key, String description, boolean defaultValue,
+    DeviceFlag(String key, String description, FlagState defaultValue,
             boolean currentValue, boolean defaultValueInCode) {
         super(key, description, defaultValue, currentValue);
         mDefaultValueInCode = defaultValueInCode;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java
index b7fb2ed..9c59361 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java
@@ -17,11 +17,15 @@
 package com.android.launcher3.uioverrides.flags;
 
 import static com.android.launcher3.config.FeatureFlags.FLAGS_PREF_NAME;
+import static com.android.launcher3.config.FeatureFlags.FlagState.TEAMFOOD;
+import static com.android.launcher3.uioverrides.flags.FlagsFactory.TEAMFOOD_FLAG;
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.os.Handler;
 import android.os.Process;
 import android.text.Html;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -30,6 +34,7 @@
 import androidx.preference.PreferenceDataStore;
 import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceViewHolder;
 import androidx.preference.SwitchPreference;
 
 import com.android.launcher3.R;
@@ -59,12 +64,7 @@
 
         @Override
         public boolean getBoolean(String key, boolean defaultValue) {
-            for (DebugFlag flag : FlagsFactory.getDebugFlags()) {
-                if (flag.key.equals(key)) {
-                    return mSharedPreferences.getBoolean(key, flag.defaultValue);
-                }
-            }
-            return defaultValue;
+            return mSharedPreferences.getBoolean(key, defaultValue);
         }
     };
 
@@ -87,18 +87,41 @@
                     : f1.key.compareToIgnoreCase(f2.key);
         });
 
+        // Ensure that teamfood flag comes on the top
+        if (flags.remove(TEAMFOOD_FLAG)) {
+            flags.add(0, (DebugFlag) TEAMFOOD_FLAG);
+        }
+
         // For flag overrides we only want to store when the engineer chose to override the
         // flag with a different value than the default. That way, when we flip flags in
         // future, engineers will pick up the new value immediately. To accomplish this, we use a
         // custom preference data store.
         for (DebugFlag flag : flags) {
-            SwitchPreference switchPreference = new SwitchPreference(mContext);
+            SwitchPreference switchPreference = new SwitchPreference(mContext) {
+                @Override
+                public void onBindViewHolder(PreferenceViewHolder holder) {
+                    super.onBindViewHolder(holder);
+                    holder.itemView.setOnLongClickListener(v -> {
+                        mSharedPreferences.edit().remove(flag.key).apply();
+                        setChecked(getFlagStateFromSharedPrefs(flag));
+                        updateSummary(this, flag);
+                        updateMenu();
+                        return true;
+                    });
+                }
+            };
             switchPreference.setKey(flag.key);
-            switchPreference.setDefaultValue(flag.defaultValue);
+            switchPreference.setDefaultValue(FlagsFactory.getEnabledValue(flag.defaultValue));
             switchPreference.setChecked(getFlagStateFromSharedPrefs(flag));
             switchPreference.setTitle(flag.key);
             updateSummary(switchPreference, flag);
             switchPreference.setPreferenceDataStore(mDataStore);
+            switchPreference.setOnPreferenceChangeListener((p, v) -> {
+                new Handler().post(() -> updateSummary(switchPreference, flag));
+                return true;
+            });
+
+
             parent.addPreference(switchPreference);
         }
         updateMenu();
@@ -108,10 +131,15 @@
      * Updates the summary to show the description and whether the flag overrides the default value.
      */
     private void updateSummary(SwitchPreference switchPreference, DebugFlag flag) {
-        String onWarning = flag.defaultValue ? "" : "<b>OVERRIDDEN</b><br>";
-        String offWarning = flag.defaultValue ? "<b>OVERRIDDEN</b><br>" : "";
-        switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.description));
-        switchPreference.setSummaryOff(Html.fromHtml(offWarning + flag.description));
+        String summary = flag.defaultValue == TEAMFOOD
+                ? "<font color='blue'><b>[TEAMFOOD]</b> </font>" : "";
+        if (mSharedPreferences.contains(flag.key)) {
+            summary += "<font color='red'><b>[OVERRIDDEN]</b> </font>";
+        }
+        if (!TextUtils.isEmpty(summary)) {
+            summary += "<br>";
+        }
+        switchPreference.setSummary(Html.fromHtml(summary + flag.description));
     }
 
     private void updateMenu() {
@@ -143,7 +171,8 @@
     }
 
     private boolean getFlagStateFromSharedPrefs(DebugFlag flag) {
-        return mDataStore.getBoolean(flag.key, flag.defaultValue);
+        boolean defaultValue = FlagsFactory.getEnabledValue(flag.defaultValue);
+        return mDataStore.getBoolean(flag.key, defaultValue);
     }
 
     private boolean anyChanged() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
index f748e8a..d066abe 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
@@ -18,6 +18,10 @@
 
 import static android.app.ActivityThread.currentApplication;
 
+import static com.android.launcher3.BuildConfig.IS_DEBUG_DEVICE;
+import static com.android.launcher3.config.FeatureFlags.FlagState.DISABLED;
+import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED;
+import static com.android.launcher3.config.FeatureFlags.FlagState.TEAMFOOD;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.content.Context;
@@ -26,8 +30,8 @@
 import android.provider.DeviceConfig.Properties;
 import android.util.Log;
 
-import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags.BooleanFlag;
+import com.android.launcher3.config.FeatureFlags.FlagState;
 import com.android.launcher3.config.FeatureFlags.IntFlag;
 import com.android.launcher3.util.ScreenOnTracker;
 
@@ -53,6 +57,9 @@
 
     private static final List<DebugFlag> sDebugFlags = new ArrayList<>();
 
+    static final BooleanFlag TEAMFOOD_FLAG = getReleaseFlag(
+            0, "LAUNCHER_TEAMFOOD", DISABLED, "Enable this flag to opt-in all team food flags");
+
     private final Set<String> mKeySet = new HashSet<>();
     private boolean mRestartRequested = false;
 
@@ -64,35 +71,54 @@
                 NAMESPACE_LAUNCHER, UI_HELPER_EXECUTOR, this::onPropertiesChanged);
     }
 
-    /**
-     * Creates a new debug flag
-     */
-    public static BooleanFlag getDebugFlag(
-            int bugId, String key, boolean defaultValue, String description) {
-        if (Utilities.IS_DEBUG_DEVICE) {
-            SharedPreferences prefs = currentApplication()
-                    .getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE);
-            boolean currentValue = prefs.getBoolean(key, defaultValue);
-            DebugFlag flag = new DebugFlag(key, description, defaultValue, currentValue);
-            sDebugFlags.add(flag);
-            return flag;
+    static boolean getEnabledValue(FlagState flagState) {
+        if (IS_DEBUG_DEVICE) {
+            switch (flagState) {
+                case ENABLED:
+                    return true;
+                case TEAMFOOD:
+                    return TEAMFOOD_FLAG.get();
+                default:
+                    return false;
+            }
         } else {
-            return new BooleanFlag(defaultValue);
+            return flagState == ENABLED;
         }
     }
 
     /**
-     * Creates a new release flag
+     * Creates a new debug flag. Debug flags always take their default value in release builds. On
+     * dogfood builds, they can be manually turned on using the flag toggle UI.
+     */
+    public static BooleanFlag getDebugFlag(
+            int bugId, String key, FlagState flagState, String description) {
+        if (IS_DEBUG_DEVICE) {
+            SharedPreferences prefs = currentApplication()
+                    .getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE);
+            boolean defaultValue = getEnabledValue(flagState);
+            boolean currentValue = prefs.getBoolean(key, defaultValue);
+            DebugFlag flag = new DebugFlag(key, description, flagState, currentValue);
+            sDebugFlags.add(flag);
+            return flag;
+        } else {
+            return new BooleanFlag(getEnabledValue(flagState));
+        }
+    }
+
+    /**
+     * Creates a new release flag. Release flags can be rolled out using server configurations and
+     * also allow manual overrides on debug builds.
      */
     public static BooleanFlag getReleaseFlag(
-            int bugId, String key, boolean defaultValueInCode, String description) {
+            int bugId, String key, FlagState flagState, String description) {
         INSTANCE.mKeySet.add(key);
+        boolean defaultValueInCode = getEnabledValue(flagState);
         boolean defaultValue = DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, defaultValueInCode);
-        if (Utilities.IS_DEBUG_DEVICE) {
+        if (IS_DEBUG_DEVICE) {
             SharedPreferences prefs = currentApplication()
                     .getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE);
             boolean currentValue = prefs.getBoolean(key, defaultValue);
-            DebugFlag flag = new DeviceFlag(key, description, defaultValue, currentValue,
+            DebugFlag flag = new DeviceFlag(key, description, flagState, currentValue,
                     defaultValueInCode);
             sDebugFlags.add(flag);
             return flag;
@@ -111,7 +137,7 @@
     }
 
     static List<DebugFlag> getDebugFlags() {
-        if (!Utilities.IS_DEBUG_DEVICE) {
+        if (!IS_DEBUG_DEVICE) {
             return Collections.emptyList();
         }
         synchronized (sDebugFlags) {
@@ -123,7 +149,7 @@
      * Dumps the current flags state to the print writer
      */
     public static void dump(PrintWriter pw) {
-        if (!Utilities.IS_DEBUG_DEVICE) {
+        if (!IS_DEBUG_DEVICE) {
             return;
         }
         pw.println("DeviceFlags:");
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index ce41c60..fd7aa58 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -16,6 +16,7 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -187,7 +188,8 @@
     public abstract boolean allowMinimizeSplitScreen();
 
     public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
-        return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons();
+        return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons()
+                || isTrackpadMultiFingerSwipe(ev);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 3f8da56..253341d 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -267,7 +267,8 @@
                 new RemoteAnimationAdapter(wrapper, RECENTS_LAUNCH_DURATION,
                         RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
                                 - STATUS_BAR_TRANSITION_PRE_DELAY),
-                new RemoteTransition(wrapper.toRemoteTransition(), getIApplicationThread()));
+                new RemoteTransition(wrapper.toRemoteTransition(), getIApplicationThread(),
+                        "LaunchFromRecents"));
         final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(options,
                 onEndCallback);
         activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
@@ -405,7 +406,8 @@
                 getMainThreadHandler(), mAnimationToHomeFactory, true);
         ActivityOptions options = ActivityOptions.makeRemoteAnimation(
                 new RemoteAnimationAdapter(runner, HOME_APPEAR_DURATION, 0),
-                new RemoteTransition(runner.toRemoteTransition(), getIApplicationThread()));
+                new RemoteTransition(runner.toRemoteTransition(), getIApplicationThread(),
+                        "StartHomeFromRecents"));
         startHomeIntentSafely(this, options.toBundle());
     }
 
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 7cc3e11..1f99d6e 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -766,7 +766,7 @@
      */
     @Nullable
     public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
-        if (mSplitScreen != null) {
+        if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS && mSplitScreen != null) {
             try {
                 return mSplitScreen.onGoingToRecentsLegacy(apps);
             } catch (RemoteException e) {
@@ -1117,6 +1117,9 @@
      */
     public boolean startRecentsActivity(Intent intent, ActivityOptions options,
             RecentsAnimationListener listener) {
+        if (mRecentTasks == null) {
+            return false;
+        }
         final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() {
             @Override
             public void onAnimationStart(IRecentsAnimationController controller,
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 6e47ff4..2aa0be6 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -129,7 +129,8 @@
             TaskShortcutFactory.PIN,
             TaskShortcutFactory.INSTALL,
             TaskShortcutFactory.FREE_FORM,
-            TaskShortcutFactory.WELLBEING
+            TaskShortcutFactory.WELLBEING,
+            TaskShortcutFactory.SAVE_APP_PAIR
     };
 
     /**
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 255b910..fd7b343 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -40,6 +40,7 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.model.WellbeingModel;
 import com.android.launcher3.popup.SystemShortcut;
@@ -63,7 +64,8 @@
 import java.util.stream.Collectors;
 
 /**
- * Represents a system shortcut that can be shown for a recent task.
+ * Represents a system shortcut that can be shown for a recent task. Appears as a single entry in
+ * the dropdown menu that shows up when you tap an app icon in Overview.
  */
 public interface TaskShortcutFactory {
     @Nullable
@@ -122,6 +124,26 @@
         }
     }
 
+    /**
+     * A menu item, "Save app pair", that allows the user to preserve the current app combination as
+     * a single persistent icon on the Home screen, allowing for quick split screen initialization.
+     */
+    class SaveAppPairSystemShortcut extends SystemShortcut {
+
+        private final TaskView mTaskView;
+
+        public SaveAppPairSystemShortcut(BaseDraggingActivity target, TaskView taskView) {
+            super(R.drawable.ic_save_app_pair, R.string.save_app_pair, target,
+                    taskView.getItemInfo(), taskView);
+            mTaskView = taskView;
+        }
+
+        @Override
+        public void onClick(View view) {
+            // TODO (b/274189428): Call "saveAppPair" function in new AppPairController class
+        }
+    }
+
     class FreeformSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
         private static final String TAG = "FreeformSystemShortcut";
 
@@ -257,9 +279,6 @@
             final PagedOrientationHandler orientationHandler =
                     recentsView.getPagedOrientationHandler();
 
-            int[] taskViewTaskIds = taskView.getTaskIds();
-            boolean taskViewHasMultipleTasks = taskViewTaskIds[0] != -1 &&
-                    taskViewTaskIds[1] != -1;
             boolean notEnoughTasksToSplit = recentsView.getTaskViewCount() < 2;
             boolean isFocusedTask = deviceProfile.isTablet && taskView.isFocusedTask();
             boolean isTaskInExpectedScrollPosition =
@@ -267,11 +286,11 @@
             boolean isTaskSplitNotSupported = !task.isDockable;
             boolean hideForExistingMultiWindow = activity.getDeviceProfile().isMultiWindowMode;
 
-            if (taskViewHasMultipleTasks ||
-                    notEnoughTasksToSplit ||
-                    isTaskSplitNotSupported ||
-                    hideForExistingMultiWindow ||
-                    (isFocusedTask && isTaskInExpectedScrollPosition)) {
+            if (taskView.containsMultipleTasks()
+                    || notEnoughTasksToSplit
+                    || isTaskSplitNotSupported
+                    || hideForExistingMultiWindow
+                    || (isFocusedTask && isTaskInExpectedScrollPosition)) {
                 return null;
             }
 
@@ -283,6 +302,26 @@
         }
     };
 
+    TaskShortcutFactory SAVE_APP_PAIR = new TaskShortcutFactory() {
+        @Nullable
+        @Override
+        public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+                TaskIdAttributeContainer taskContainer) {
+            final TaskView taskView = taskContainer.getTaskView();
+
+            if (!FeatureFlags.ENABLE_APP_PAIRS.get() || !taskView.containsMultipleTasks()) {
+                return null;
+            }
+
+            return Collections.singletonList(new SaveAppPairSystemShortcut(activity, taskView));
+        }
+
+        @Override
+        public boolean showForSplitscreen() {
+            return true;
+        }
+    };
+
     TaskShortcutFactory FREE_FORM = new TaskShortcutFactory() {
         @Override
         public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 6dd67de..b76fe5c 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -24,6 +24,7 @@
 import com.android.launcher3.DeviceProfile
 import com.android.launcher3.anim.PendingAnimation
 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource
+import com.android.quickstep.views.IconView
 import com.android.quickstep.views.TaskThumbnailView
 import com.android.quickstep.views.TaskView
 import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
@@ -52,21 +53,22 @@
      * depending on the state of the surface from which the split was initiated
      */
     fun getFirstAnimInitViews(taskViewSupplier: Supplier<TaskView>,
-                              splitSelectSourceSupplier: Supplier<SplitSelectSource>)
+                              splitSelectSourceSupplier: Supplier<SplitSelectSource?>)
             : SplitAnimInitProps {
+        val splitSelectSource = splitSelectSourceSupplier.get()
         if (!splitSelectStateController.isAnimateCurrentTaskDismissal) {
             // Initiating from home
-            val splitSelectSource = splitSelectSourceSupplier.get()
-            return SplitAnimInitProps(splitSelectSource.view, originalBitmap = null,
+            return SplitAnimInitProps(splitSelectSource!!.view, originalBitmap = null,
                     splitSelectSource.drawable, fadeWithThumbnail = false, isStagedTask = true,
                     iconView = null)
         } else if (splitSelectStateController.isDismissingFromSplitPair) {
             // Initiating split from overview, but on a split pair
             val taskView = taskViewSupplier.get()
             for (container : TaskIdAttributeContainer in taskView.taskIdAttributeContainers) {
-                if (container.task.key.id == splitSelectStateController.initialTaskId) {
+                if (container.task.getKey().getId() == splitSelectStateController.initialTaskId) {
+                    val drawable = getDrawable(container.iconView, splitSelectSource)
                     return SplitAnimInitProps(container.thumbnailView,
-                            container.thumbnailView.thumbnail, container.iconView.drawable!!,
+                            container.thumbnailView.thumbnail, drawable!!,
                             fadeWithThumbnail = true, isStagedTask = true,
                             iconView = container.iconView
                     )
@@ -77,14 +79,28 @@
         } else {
             // Initiating split from overview on fullscreen task TaskView
             val taskView = taskViewSupplier.get()
+            val drawable = getDrawable(taskView.iconView, splitSelectSource)
             return SplitAnimInitProps(taskView.thumbnail, taskView.thumbnail.thumbnail,
-                    taskView.iconView.drawable!!, fadeWithThumbnail = true, isStagedTask = true,
+                    drawable!!, fadeWithThumbnail = true, isStagedTask = true,
                     taskView.iconView
             )
         }
     }
 
     /**
+     * Returns the drawable that's provided in iconView, however if that
+     * is null it falls back to the drawable that's in splitSelectSource.
+     * TaskView's icon drawable can be null if the TaskView is scrolled far enough off screen
+     * @return [Drawable]
+     */
+    fun getDrawable(iconView: IconView, splitSelectSource: SplitSelectSource?) : Drawable? {
+        if (iconView.drawable == null && splitSelectSource != null) {
+            return splitSelectSource.drawable
+        }
+        return iconView.drawable
+    }
+
+    /**
      * When selecting first app from split pair, second app's thumbnail remains. This animates
      * the second thumbnail by expanding it to take up the full taskViewWidth/Height and overlaying
      * it with [TaskThumbnailView]'s splashView. Adds animations to the provided builder.
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 00d6571..c537ef8 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -284,7 +284,8 @@
             final RemoteSplitLaunchTransitionRunner animationRunner =
                     new RemoteSplitLaunchTransitionRunner(taskId1, taskId2, callback);
             final RemoteTransition remoteTransition = new RemoteTransition(animationRunner,
-                    ActivityThread.currentActivityThread().getApplicationThread());
+                    ActivityThread.currentActivityThread().getApplicationThread(),
+                    "LaunchSplitPair");
             if (intent1 == null && intent2 == null) {
                 mSystemUiProxy.startTasks(taskId1, options1.toBundle(), taskId2,
                         null /* options2 */, stagePosition, splitRatio, remoteTransition,
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 9a25e9c..7989bb0 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -185,6 +185,7 @@
 import com.android.quickstep.util.DesktopTask;
 import com.android.quickstep.util.GroupTask;
 import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.RecentsAtomicAnimationFactory;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.SplitAnimationController.Companion.SplitAnimInitProps;
 import com.android.quickstep.util.SplitAnimationTimings;
@@ -801,8 +802,29 @@
 
         // if multi-instance feature is enabled
         if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
-            // invalidate the current list of tasks if filter changes
-            mFilterState.setOnFilterUpdatedListener(this::invalidateTaskList);
+            // invalidate the current list of tasks if filter changes with a fading in/out animation
+            mFilterState.setOnFilterUpdatedListener(() -> {
+                Animator animatorFade = mActivity.getStateManager().createStateElementAnimation(
+                        RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM, 1f, 0f);
+                Animator animatorAppear = mActivity.getStateManager().createStateElementAnimation(
+                        RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM, 0f, 1f);
+                animatorFade.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(@NonNull Animator animation) {
+                        RecentsView.this.invalidateTaskList();
+                        updateClearAllFunction();
+                        reloadIfNeeded();
+                        if (mPendingAnimation != null) {
+                            mPendingAnimation.addEndListener(success -> {
+                                animatorAppear.start();
+                            });
+                        } else {
+                            animatorAppear.start();
+                        }
+                    }
+                });
+                animatorFade.start();
+            });
         }
         // make sure filter is turned off by default
         mFilterState.setFilterBy(null);
@@ -821,8 +843,6 @@
      */
     public void setAndApplyFilter(@Nullable String packageName) {
         mFilterState.setFilterBy(packageName);
-        updateClearAllFunction();
-        reloadIfNeeded();
     }
 
     /**
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 9a65b4f..c39d095 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -186,35 +186,6 @@
         actionsView.clickAndDismissScreenshot();
     }
 
-    @Test
-    @PortraitLandscape
-    public void testSplitFromOverview() {
-        assumeTrue(!mLauncher.isTablet());
-
-        startTestActivity(2);
-        startTestActivity(3);
-
-        mLauncher.goHome().switchToOverview().getCurrentTask()
-                .tapMenu()
-                .tapSplitMenuItem()
-                .getCurrentTask()
-                .open();
-    }
-
-    @Test
-    @PortraitLandscape
-    public void testSplitFromOverviewForTablet() {
-        assumeTrue(mLauncher.isTablet());
-
-        startTestActivity(2);
-        startTestActivity(3);
-
-        mLauncher.goHome().switchToOverview().getOverviewActions()
-                .clickSplit()
-                .getTestActivityTask(2)
-                .open();
-    }
-
     private int getCurrentOverviewPage(Launcher launcher) {
         return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
     }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index d3fbe93..2ae512a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -15,14 +15,18 @@
  */
 package com.android.quickstep;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
 import android.content.Intent;
 
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.ui.TaplTestsLauncher3;
 import com.android.launcher3.util.rule.TestStabilityRule;
 import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
 
 import org.junit.After;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -37,13 +41,6 @@
         super.setUp();
         TaplTestsLauncher3.initialize(this);
 
-        mLauncher.getWorkspace()
-                .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
-                .switchToAllApps()
-                .getAppIcon(CALCULATOR_APP_NAME)
-                .dragToHotseat(0);
-
-        startAppFast(CALCULATOR_APP_PACKAGE);
         if (mLauncher.isTablet()) {
             mLauncher.enableBlockTimeout(true);
             mLauncher.showTaskbarIfHidden();
@@ -58,14 +55,29 @@
     }
 
     @Test
+    @PortraitLandscape
+    public void testSplitFromOverview() {
+        createAndLaunchASplitPair();
+    }
+
+    @Test
     // TODO (b/270201357): When this test is proven stable, remove this TestStabilityRule and
-    // introduce into presubmit as well.
+    //  introduce into presubmit as well.
     @TestStabilityRule.Stability(
             flavors = TestStabilityRule.LOCAL | TestStabilityRule.PLATFORM_POSTSUBMIT)
     @PortraitLandscape
     @TaskbarModeSwitch
     public void testSplitAppFromHomeWithItself() throws Exception {
-        Assume.assumeTrue(mLauncher.isTablet());
+        // Currently only tablets have Taskbar in Overview, so test is only active on tablets
+        assumeTrue(mLauncher.isTablet());
+
+        mLauncher.getWorkspace()
+                .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
+                .switchToAllApps()
+                .getAppIcon(CALCULATOR_APP_NAME)
+                .dragToHotseat(0);
+
+        startAppFast(CALCULATOR_APP_PACKAGE);
 
         mLauncher.goHome()
                 .switchToAllApps()
@@ -79,4 +91,50 @@
                 .getAppIcon(CALCULATOR_APP_NAME)
                 .launchIntoSplitScreen();
     }
+
+    @Test
+    public void testSaveAppPairMenuItemExistsOnSplitPair() throws Exception {
+        assumeTrue(FeatureFlags.ENABLE_APP_PAIRS.get());
+
+        createAndLaunchASplitPair();
+
+        assertTrue("Save app pair menu item is missing",
+                mLauncher.goHome()
+                        .switchToOverview()
+                        .getCurrentTask()
+                        .tapMenu()
+                        .hasMenuItem("Save app pair"));
+    }
+
+    @Test
+    public void testSaveAppPairMenuItemDoesNotExistOnSingleTask() throws Exception {
+        assumeTrue(FeatureFlags.ENABLE_APP_PAIRS.get());
+
+        startAppFast(CALCULATOR_APP_PACKAGE);
+
+        assertFalse("Save app pair menu item is erroneously appearing on single task",
+                mLauncher.goHome()
+                        .switchToOverview()
+                        .getCurrentTask()
+                        .tapMenu()
+                        .hasMenuItem("Save app pair"));
+    }
+
+    private void createAndLaunchASplitPair() {
+        startTestActivity(2);
+        startTestActivity(3);
+
+        if (mLauncher.isTablet()) {
+            mLauncher.goHome().switchToOverview().getOverviewActions()
+                    .clickSplit()
+                    .getTestActivityTask(2)
+                    .open();
+        } else {
+            mLauncher.goHome().switchToOverview().getCurrentTask()
+                    .tapMenu()
+                    .tapSplitMenuItem()
+                    .getCurrentTask()
+                    .open();
+        }
+    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
new file mode 100644
index 0000000..7e07b81
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
@@ -0,0 +1,165 @@
+/*
+ *  Copyright (C) 2023 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package com.android.quickstep.util
+
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.quickstep.views.GroupedTaskView
+import com.android.quickstep.views.IconView
+import com.android.quickstep.views.TaskThumbnailView
+import com.android.quickstep.views.TaskView
+import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
+import com.android.systemui.shared.recents.model.Task
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class SplitAnimationControllerTest {
+
+    private val taskId = 9
+
+    @Mock lateinit var mockSplitSelectStateController: SplitSelectStateController
+    // TaskView
+    @Mock lateinit var mockTaskView: TaskView
+    @Mock lateinit var mockThumbnailView: TaskThumbnailView
+    @Mock lateinit var mockBitmap: Bitmap
+    @Mock lateinit var mockIconView: IconView
+    @Mock lateinit var mockTaskViewDrawable: Drawable
+    // GroupedTaskView
+    @Mock lateinit var mockGroupedTaskView: GroupedTaskView
+    @Mock lateinit var mockTask: Task
+    @Mock lateinit var mockTaskKey: Task.TaskKey
+    @Mock lateinit var mockTaskIdAttributeContainer: TaskIdAttributeContainer
+
+    // SplitSelectSource
+    @Mock lateinit var splitSelectSource: SplitConfigurationOptions.SplitSelectSource
+    @Mock lateinit var mockSplitSourceDrawable: Drawable
+    @Mock lateinit var mockSplitSourceView: View
+
+    lateinit var splitAnimationController: SplitAnimationController
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        whenever(mockTaskView.thumbnail).thenReturn(mockThumbnailView)
+        whenever(mockThumbnailView.thumbnail).thenReturn(mockBitmap)
+        whenever(mockTaskView.iconView).thenReturn(mockIconView)
+        whenever(mockIconView.drawable).thenReturn(mockTaskViewDrawable)
+
+        whenever(splitSelectSource.drawable).thenReturn(mockSplitSourceDrawable)
+        whenever(splitSelectSource.view).thenReturn(mockSplitSourceView)
+
+        splitAnimationController = SplitAnimationController(mockSplitSelectStateController)
+    }
+
+    @Test
+    fun getFirstAnimInitViews_nullTaskViewIcon_useSplitSourceIcon() {
+        // Hit fullscreen task dismissal state
+        whenever(mockSplitSelectStateController.isAnimateCurrentTaskDismissal).thenReturn(true)
+        whenever(mockSplitSelectStateController.isDismissingFromSplitPair).thenReturn(false)
+
+        // Missing taskView icon
+        whenever(mockIconView.drawable).thenReturn(null)
+
+        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
+                splitAnimationController.getFirstAnimInitViews(
+                        { mockTaskView }, { splitSelectSource })
+
+        assertEquals("Did not fallback to use splitSource icon drawable",
+                mockSplitSourceDrawable, splitAnimInitProps.iconDrawable)
+    }
+
+    @Test
+    fun getFirstAnimInitViews_validTaskViewIcon_useTaskViewIcon() {
+        // Hit fullscreen task dismissal state
+        whenever(mockSplitSelectStateController.isAnimateCurrentTaskDismissal).thenReturn(true)
+        whenever(mockSplitSelectStateController.isDismissingFromSplitPair).thenReturn(false)
+
+        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
+                splitAnimationController.getFirstAnimInitViews(
+                        { mockTaskView }, { splitSelectSource })
+
+        assertEquals("Did not use taskView icon drawable", mockTaskViewDrawable,
+                splitAnimInitProps.iconDrawable)
+    }
+
+    @Test
+    fun getFirstAnimInitViews_validTaskViewNullSplitSource_useTaskViewIcon() {
+        // Hit fullscreen task dismissal state
+        whenever(mockSplitSelectStateController.isAnimateCurrentTaskDismissal).thenReturn(true)
+        whenever(mockSplitSelectStateController.isDismissingFromSplitPair).thenReturn(false)
+
+        // Set split source to null
+        whenever(splitSelectSource.drawable).thenReturn(null)
+
+        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
+                splitAnimationController.getFirstAnimInitViews(
+                        { mockTaskView }, { splitSelectSource })
+
+        assertEquals("Did not use taskView icon drawable", mockTaskViewDrawable,
+                splitAnimInitProps.iconDrawable)
+    }
+
+    @Test
+    fun getFirstAnimInitViews_nullTaskViewValidSplitSource_noTaskDismissal() {
+        // Hit initiating split from home
+        whenever(mockSplitSelectStateController.isAnimateCurrentTaskDismissal).thenReturn(false)
+        whenever(mockSplitSelectStateController.isDismissingFromSplitPair).thenReturn(false)
+
+        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
+                splitAnimationController.getFirstAnimInitViews(
+                        { mockTaskView }, { splitSelectSource })
+
+        assertEquals("Did not use splitSource icon drawable", mockSplitSourceDrawable,
+                splitAnimInitProps.iconDrawable)
+    }
+
+    @Test
+    fun getFirstAnimInitViews_nullTaskViewValidSplitSource_groupedTaskView() {
+        // Hit groupedTaskView dismissal
+        whenever(mockSplitSelectStateController.isAnimateCurrentTaskDismissal).thenReturn(true)
+        whenever(mockSplitSelectStateController.isDismissingFromSplitPair).thenReturn(true)
+
+        // Remove icon view from GroupedTaskView
+        whenever(mockIconView.drawable).thenReturn(null)
+
+        whenever(mockTaskIdAttributeContainer.task).thenReturn(mockTask)
+        whenever(mockTaskIdAttributeContainer.iconView).thenReturn(mockIconView)
+        whenever(mockTaskIdAttributeContainer.thumbnailView).thenReturn(mockThumbnailView)
+        whenever(mockTask.getKey()).thenReturn(mockTaskKey)
+        whenever(mockTaskKey.getId()).thenReturn(taskId)
+        whenever(mockSplitSelectStateController.initialTaskId).thenReturn(taskId)
+        whenever(mockGroupedTaskView.taskIdAttributeContainers)
+                .thenReturn(Array(1) { mockTaskIdAttributeContainer })
+        val splitAnimInitProps : SplitAnimationController.Companion.SplitAnimInitProps =
+                splitAnimationController.getFirstAnimInitViews(
+                        { mockGroupedTaskView }, { splitSelectSource })
+
+        assertEquals("Did not use splitSource icon drawable", mockSplitSourceDrawable,
+                splitAnimInitProps.iconDrawable)
+    }
+}
\ No newline at end of file
diff --git a/res/drawable/page_indicator.xml b/res/drawable/page_indicator.xml
deleted file mode 100644
index c0ccc49..0000000
--- a/res/drawable/page_indicator.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <solid android:color="?attr/folderPaginationColor"/>
-    <size android:width="@dimen/page_indicator_size" android:height="@dimen/page_indicator_size"/>
-</shape>
\ No newline at end of file
diff --git a/res/layout/page_indicator_dots.xml b/res/layout/page_indicator_dots.xml
index d5fe51e..41844b7 100644
--- a/res/layout/page_indicator_dots.xml
+++ b/res/layout/page_indicator_dots.xml
@@ -14,9 +14,13 @@
      limitations under the License.
 -->
 
-<com.android.launcher3.pageindicators.PageIndicatorDots xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.pageindicators.LauncherDotsPageIndicator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:id="@+id/page_indicator"
     android:layout_width="match_parent"
     android:layout_height="@dimen/workspace_page_indicator_height"
     android:layout_gravity="bottom | center_horizontal"
-    android:theme="@style/HomeScreenElementTheme" />
\ No newline at end of file
+    android:theme="@style/HomeScreenElementTheme"
+    launcher:indicatorDotColor="?attr/workspaceTextColor"
+    />
\ No newline at end of file
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 5518dc8..35ccc5a 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -53,12 +53,13 @@
             android:textColorHighlight="?android:attr/colorControlHighlight"
             android:textColorHint="?attr/folderHintColor"/>
 
-        <com.android.launcher3.pageindicators.PageIndicatorDots
+        <com.android.launcher3.pageindicators.LauncherDotsPageIndicator
             android:id="@+id/folder_page_indicator"
             android:layout_gravity="center_vertical"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:elevation="1dp"
+            launcher:indicatorDotColor="@color/folder_pagination_color"
             />
 
     </LinearLayout>
diff --git a/res/values-night-v31/colors.xml b/res/values-night-v31/colors.xml
index f331361..3b85b88 100644
--- a/res/values-night-v31/colors.xml
+++ b/res/values-night-v31/colors.xml
@@ -26,4 +26,6 @@
     <color name="home_settings_track_off_color">@android:color/system_neutral1_700</color>
 
     <color name="all_apps_button_color">@android:color/system_neutral2_200</color>
+
+    <color name="folder_pagination_color">@android:color/system_accent2_100</color>
 </resources>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 17fe419..ee27d99 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -18,4 +18,6 @@
 
 <resources>
     <color name="all_apps_button_color">#BFC8CC</color>
+    <color name="folder_pagination_color">#ffbfebe3</color>
+
 </resources>
\ No newline at end of file
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 054fe47..0c87ff4 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -41,8 +41,7 @@
     <color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
 
     <color name="folder_dot_color">@android:color/system_accent3_100</color>
-    <color name="folder_pagination_color_light">@android:color/system_accent1_600</color>
-    <color name="folder_pagination_color_dark">@android:color/system_accent2_100</color>
+    <color name="folder_pagination_color">@android:color/system_accent1_600</color>
 
     <color name="home_settings_header_accent">@android:color/system_accent1_600</color>
     <color name="home_settings_header_collapsed">@android:color/system_neutral1_100</color>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 682153f..04eb0db 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -42,7 +42,6 @@
     <attr name="popupNotificationDotColor" format="color" />
 
     <attr name="folderDotColor" format="color" />
-    <attr name="folderPaginationColor" format="color" />
     <attr name="folderPreviewColor" format="color" />
     <attr name="folderBackgroundColor" format="color" />
     <attr name="folderIconRadius" format="float" />
@@ -491,4 +490,10 @@
         <!-- The icon drawable of a widget category. -->
         <attr name="sectionDrawable" format="reference" />
     </declare-styleable>
+
+    <!-- Attributes for PagedIndicator -->
+    <declare-styleable name="PagedIndicator">
+        <!-- Color used to draw dots -->
+        <attr name="indicatorDotColor" format="color" />
+    </declare-styleable>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 8788557..1ef918d 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -67,8 +67,7 @@
     <color name="folder_preview_dark">#464746</color>
 
     <color name="folder_dot_color">?attr/colorPrimary</color>
-    <color name="folder_pagination_color_light">#ff006c5f</color>
-    <color name="folder_pagination_color_dark">#ffbfebe3</color>
+    <color name="folder_pagination_color">#ff006c5f</color>
 
     <color name="text_color_primary_dark">#FFFFFFFF</color>
     <color name="text_color_secondary_dark">#FFFFFFFF</color>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f2fb8f5..7552b22 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -42,6 +42,9 @@
     <string name="recent_task_option_split_screen">Split screen</string>
     <string name="split_app_info_accessibility">App info for %1$s</string>
 
+    <!-- App pairs -->
+    <string name="save_app_pair">Save app pair</string>
+
     <!-- Widgets -->
     <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
     <string name="long_press_widget_to_add">Touch &amp; hold to move a widget.</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 5dc4f0a..517bb87 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,7 +50,6 @@
         <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
         <item name="folderDotColor">@color/folder_dot_color</item>
-        <item name="folderPaginationColor">@color/folder_pagination_color_light</item>
         <item name="folderPreviewColor">@color/folder_preview_light</item>
         <item name="folderBackgroundColor">@color/folder_background_light</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
@@ -104,7 +103,6 @@
         <item name="popupShadeThird">@color/popup_shade_third_dark</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
         <item name="folderDotColor">@color/folder_dot_color</item>
-        <item name="folderPaginationColor">@color/folder_pagination_color_dark</item>
         <item name="folderPreviewColor">@color/folder_preview_dark</item>
         <item name="folderBackgroundColor">@color/folder_background_dark</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2e382c8..7beac0b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,7 +85,6 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
@@ -1353,10 +1352,6 @@
 
         if (SHOW_DOT_PAGINATION.get()) {
             mWorkspace.getPageIndicator().setShouldAutoHide(true);
-            mWorkspace.getPageIndicator().setPaintColor(
-                    Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)
-                            ? Color.BLACK
-                            : Color.WHITE);
         }
     }
 
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 62e7ef3..26ad4b5 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -153,8 +153,8 @@
         Interpolator workspaceFadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
                 pageAlphaProvider.interpolator);
         float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
-        propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
-                workspacePageIndicatorAlpha, workspaceFadeInterpolator);
+        mLauncher.getWorkspace().getPageIndicator().setAlpha(
+                propertySetter, workspacePageIndicatorAlpha, workspaceFadeInterpolator);
         Interpolator hotseatFadeInterpolator = config.getInterpolator(ANIM_HOTSEAT_FADE,
                 workspaceFadeInterpolator);
         float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index c3ac53e..c5cf647 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -192,7 +192,6 @@
                     onAppsUpdated);
         }
         mAllAppsStore.addUpdateListener(onAppsUpdated);
-        mActivityContext.addOnDeviceProfileChangeListener(this);
 
         // This is a focus listener that proxies focus from a view into the list view.  This is to
         // work around the search box from getting first focus and showing the cursor.
@@ -263,6 +262,18 @@
         mSearchUiManager.initializeSearch(this);
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mActivityContext.addOnDeviceProfileChangeListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mActivityContext.removeOnDeviceProfileChangeListener(this);
+    }
+
     public SearchUiManager getSearchUiManager() {
         return mSearchUiManager;
     }
@@ -347,16 +358,10 @@
 
     public boolean shouldContainerScroll(MotionEvent ev) {
         BaseDragLayer dragLayer = mActivityContext.getDragLayer();
-        // IF the MotionEvent is inside the search box, and the container keeps on receiving
-        // touch input, container should move down.
-        if (dragLayer.isEventOverView(mSearchContainer, ev)) {
-            return true;
-        }
-        // Scroll if not within the container view (e.g. over large-screen scrim).
-        if (!dragLayer.isEventOverView(getVisibleContainerView(), ev)) {
-            return true;
-        }
-        if (dragLayer.isEventOverView(mBottomSheetHandleArea, ev)) {
+        // IF the MotionEvent is inside the search box or handle area, and the container keeps on
+        // receiving touch input, container should move down.
+        if (dragLayer.isEventOverView(mSearchContainer, ev)
+                || dragLayer.isEventOverView(mBottomSheetHandleArea, ev)) {
             return true;
         }
         AllAppsRecyclerView rv = getActiveRecyclerView();
@@ -368,6 +373,10 @@
                 && dragLayer.isEventOverView(rv.getScrollbar(), ev)) {
             return false;
         }
+        // Scroll if not within the container view (e.g. over large-screen scrim).
+        if (!dragLayer.isEventOverView(getVisibleContainerView(), ev)) {
+            return true;
+        }
         return rv.shouldContainerScroll(ev, dragLayer);
     }
 
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 228b02b..2174936 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -65,7 +65,7 @@
      * sets highlight result's title
      */
     default void setFocusedResultTitle(
-            @Nullable CharSequence title, @Nullable CharSequence subtitle) {}
+            @Nullable CharSequence title, @Nullable CharSequence subtitle, boolean showArrow) {}
 
     /** Refresh the currently displayed list of results. */
     default void refreshResults() {}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 2972489..8cde18d 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3.config;
 
+import static com.android.launcher3.config.FeatureFlags.FlagState.DISABLED;
+import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED;
+import static com.android.launcher3.config.FeatureFlags.FlagState.TEAMFOOD;
 import static com.android.launcher3.uioverrides.flags.FlagsFactory.getDebugFlag;
 import static com.android.launcher3.uioverrides.flags.FlagsFactory.getReleaseFlag;
 
@@ -74,338 +77,326 @@
      * and set a default value for the flag. This will be the default value on Debug builds.
      */
     public static final BooleanFlag ENABLE_INPUT_CONSUMER_REASON_LOGGING = getDebugFlag(270390028,
-            "ENABLE_INPUT_CONSUMER_REASON_LOGGING",
-            true,
+            "ENABLE_INPUT_CONSUMER_REASON_LOGGING", ENABLED,
             "Log the reason why an Input Consumer was selected for a gesture.");
 
     public static final BooleanFlag ENABLE_GESTURE_ERROR_DETECTION = getDebugFlag(270389990,
-            "ENABLE_GESTURE_ERROR_DETECTION",
-            true,
+            "ENABLE_GESTURE_ERROR_DETECTION", ENABLED,
             "Analyze gesture events and log detected errors");
 
     // When enabled the promise icon is visible in all apps while installation an app.
     public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(270390012,
-            "PROMISE_APPS_IN_ALL_APPS", false, "Add promise icon in all-apps");
+            "PROMISE_APPS_IN_ALL_APPS", DISABLED, "Add promise icon in all-apps");
 
     public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag(270390904,
-            "KEYGUARD_ANIMATION", false, "Enable animation for keyguard going away on wallpaper");
+            "KEYGUARD_ANIMATION", DISABLED,
+            "Enable animation for keyguard going away on wallpaper");
 
     public static final BooleanFlag ENABLE_DEVICE_SEARCH = getReleaseFlag(270390907,
-            "ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps");
+            "ENABLE_DEVICE_SEARCH", ENABLED, "Allows on device search in all apps");
 
     public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR =
-            getReleaseFlag(270390286, "ENABLE_FLOATING_SEARCH_BAR", false,
-                    "Keep All Apps search bar at the bottom (but above keyboard if open)");
+            getReleaseFlag(270390286, "ENABLE_FLOATING_SEARCH_BAR", DISABLED,
+            "Keep All Apps search bar at the bottom (but above keyboard if open)");
 
     public static final BooleanFlag ENABLE_HIDE_HEADER = getReleaseFlag(270390930,
-            "ENABLE_HIDE_HEADER", true, "Hide header on keyboard before typing in all apps");
+            "ENABLE_HIDE_HEADER", ENABLED, "Hide header on keyboard before typing in all apps");
 
     public static final BooleanFlag ENABLE_EXPANDING_PAUSE_WORK_BUTTON = getDebugFlag(270390779,
-            "ENABLE_EXPANDING_PAUSE_WORK_BUTTON", false,
+            "ENABLE_EXPANDING_PAUSE_WORK_BUTTON", DISABLED,
             "Expand and collapse pause work button while scrolling");
 
     public static final BooleanFlag COLLECT_SEARCH_HISTORY = getReleaseFlag(270391455,
-            "COLLECT_SEARCH_HISTORY", false, "Allow launcher to collect search history for log");
+            "COLLECT_SEARCH_HISTORY", DISABLED, "Allow launcher to collect search history for log");
 
     public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937,
-            "ENABLE_TWOLINE_ALLAPPS", false, "Enables two line label inside all apps.");
+            "ENABLE_TWOLINE_ALLAPPS", DISABLED, "Enables two line label inside all apps.");
 
     public static final BooleanFlag ENABLE_TWOLINE_DEVICESEARCH = getDebugFlag(201388851,
-            "ENABLE_TWOLINE_DEVICESEARCH", false,
+            "ENABLE_TWOLINE_DEVICESEARCH", DISABLED,
             "Enable two line label for icons with labels on device search.");
 
     public static final BooleanFlag ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING = getReleaseFlag(
-            270391397, "ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING", false,
+            270391397, "ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING", DISABLED,
             "Allows on device search in all apps logging");
 
     public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(270391693,
-            "IME_STICKY_SNACKBAR_EDU", true, "Show sticky IME edu in AllApps");
+            "IME_STICKY_SNACKBAR_EDU", ENABLED, "Show sticky IME edu in AllApps");
 
     public static final BooleanFlag ENABLE_PEOPLE_TILE_PREVIEW = getDebugFlag(270391653,
-            "ENABLE_PEOPLE_TILE_PREVIEW", false,
+            "ENABLE_PEOPLE_TILE_PREVIEW", DISABLED,
             "Experimental: Shows conversation shortcuts on home screen as search results");
 
     public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag(270391638,
-            "FOLDER_NAME_MAJORITY_RANKING", true,
+            "FOLDER_NAME_MAJORITY_RANKING", ENABLED,
             "Suggests folder names based on majority based ranking.");
 
     public static final BooleanFlag INJECT_FALLBACK_APP_CORPUS_RESULTS = getReleaseFlag(270391706,
-            "INJECT_FALLBACK_APP_CORPUS_RESULTS", false,
+            "INJECT_FALLBACK_APP_CORPUS_RESULTS", DISABLED,
             "Inject fallback app corpus result when AiAi fails to return it.");
 
     public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(270391641,
-            "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
+            "ASSISTANT_GIVES_LAUNCHER_FOCUS", DISABLED,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
 
     public static final BooleanFlag ENABLE_BULK_WORKSPACE_ICON_LOADING = getDebugFlag(270392203,
-            "ENABLE_BULK_WORKSPACE_ICON_LOADING",
-            true,
+            "ENABLE_BULK_WORKSPACE_ICON_LOADING", ENABLED,
             "Enable loading workspace icons in bulk.");
 
     public static final BooleanFlag ENABLE_BULK_ALL_APPS_ICON_LOADING = getDebugFlag(270392465,
-            "ENABLE_BULK_ALL_APPS_ICON_LOADING",
-            true,
-            "Enable loading all apps icons in bulk.");
+            "ENABLE_BULK_ALL_APPS_ICON_LOADING", ENABLED, "Enable loading all apps icons in bulk.");
 
     public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(270392706,
-            "ENABLE_DATABASE_RESTORE", false,
+            "ENABLE_DATABASE_RESTORE", DISABLED,
             "Enable database restore when new restore session is created");
 
     public static final BooleanFlag ENABLE_SMARTSPACE_DISMISS = getDebugFlag(270391664,
-            "ENABLE_SMARTSPACE_DISMISS", true,
+            "ENABLE_SMARTSPACE_DISMISS", ENABLED,
             "Adds a menu option to dismiss the current Enhanced Smartspace card.");
 
     public static final BooleanFlag ENABLE_OVERLAY_CONNECTION_OPTIM = getDebugFlag(270392629,
-            "ENABLE_OVERLAY_CONNECTION_OPTIM",
-            false,
+            "ENABLE_OVERLAY_CONNECTION_OPTIM", DISABLED,
             "Enable optimizing overlay service connection");
 
     /**
      * Enables region sampling for text color: Needs system health assessment before turning on
      */
     public static final BooleanFlag ENABLE_REGION_SAMPLING = getDebugFlag(270391669,
-            "ENABLE_REGION_SAMPLING", false,
+            "ENABLE_REGION_SAMPLING", DISABLED,
             "Enable region sampling to determine color of text on screen.");
 
     public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
-            getDebugFlag(270393096,
-                    "ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false,
-                    "Always use hardware optimization for folder animations.");
+            getDebugFlag(270393096, "ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS",
+            DISABLED, "Always use hardware optimization for folder animations.");
 
     public static final BooleanFlag SEPARATE_RECENTS_ACTIVITY = getDebugFlag(270392980,
-            "SEPARATE_RECENTS_ACTIVITY", false,
+            "SEPARATE_RECENTS_ACTIVITY", DISABLED,
             "Uses a separate recents activity instead of using the integrated recents+Launcher UI");
 
     public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(270392984,
-            "ENABLE_MINIMAL_DEVICE", false,
+            "ENABLE_MINIMAL_DEVICE", DISABLED,
             "Allow user to toggle minimal device mode in launcher.");
 
-    public static final BooleanFlag ENABLE_TASKBAR_POPUP_MENU = getDebugFlag(
-            270392477, "ENABLE_TASKBAR_POPUP_MENU", true,
+    public static final BooleanFlag ENABLE_TASKBAR_POPUP_MENU = getDebugFlag(270392477,
+            "ENABLE_TASKBAR_POPUP_MENU", ENABLED,
             "Enables long pressing taskbar icons to show the popup menu.");
 
     public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(270392643,
-            "ENABLE_TWO_PANEL_HOME", true,
+            "ENABLE_TWO_PANEL_HOME", ENABLED,
             "Uses two panel on home screen. Only applicable on large screen devices.");
 
     public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag(270393276,
-            "ENABLE_SCRIM_FOR_APP_LAUNCH", false,
-            "Enables scrim during app launch animation.");
+            "ENABLE_SCRIM_FOR_APP_LAUNCH", DISABLED, "Enables scrim during app launch animation.");
 
     public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = getReleaseFlag(270393258,
-            "ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
+            "ENABLE_ENFORCED_ROUNDED_CORNERS", ENABLED,
+            "Enforce rounded corners on all App Widgets");
 
-    public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag(
-            270393108, "NOTIFY_CRASHES", false,
-            "Sends a notification whenever launcher encounters an uncaught exception.");
+    public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag(270393108, "NOTIFY_CRASHES",
+            DISABLED, "Sends a notification whenever launcher encounters an uncaught exception.");
 
     public static final BooleanFlag ENABLE_WALLPAPER_SCRIM = getDebugFlag(270393604,
-            "ENABLE_WALLPAPER_SCRIM", false,
+            "ENABLE_WALLPAPER_SCRIM", DISABLED,
             "Enables scrim over wallpaper for text protection.");
 
     public static final BooleanFlag WIDGETS_IN_LAUNCHER_PREVIEW = getDebugFlag(270393268,
-            "WIDGETS_IN_LAUNCHER_PREVIEW", true,
+            "WIDGETS_IN_LAUNCHER_PREVIEW", ENABLED,
             "Enables widgets in Launcher preview for the Wallpaper app.");
 
     public static final BooleanFlag QUICK_WALLPAPER_PICKER = getDebugFlag(270393112,
-            "QUICK_WALLPAPER_PICKER", true,
-            "Shows quick wallpaper picker in long-press menu");
+            "QUICK_WALLPAPER_PICKER", ENABLED, "Shows quick wallpaper picker in long-press menu");
 
     public static final BooleanFlag ENABLE_BACK_SWIPE_HOME_ANIMATION = getDebugFlag(270393426,
-            "ENABLE_BACK_SWIPE_HOME_ANIMATION", true,
+            "ENABLE_BACK_SWIPE_HOME_ANIMATION", ENABLED,
             "Enables home animation to icon when user swipes back.");
 
     public static final BooleanFlag ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION = getDebugFlag(270614790,
-            "ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION", false,
+            "ENABLE_BACK_SWIPE_LAUNCHER_ANIMATION", DISABLED,
             "Enables predictive back aniamtion from all apps and widgets to home");
 
     public static final BooleanFlag ENABLE_ICON_LABEL_AUTO_SCALING = getDebugFlag(270393294,
-            "ENABLE_ICON_LABEL_AUTO_SCALING", true,
+            "ENABLE_ICON_LABEL_AUTO_SCALING", ENABLED,
             "Enables scaling/spacing for icon labels to make more characters visible");
 
     public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897,
-            "ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT", false,
+            "ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT", DISABLED,
             "Enables displaying the all apps button in the hotseat.");
 
     public static final BooleanFlag ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR = getDebugFlag(270393900,
-            "ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", false,
+            "ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", DISABLED,
             "Enables One Search box in Taskbar All Apps.");
 
     public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE = getDebugFlag(270393906,
-            "ENABLE_SPLIT_FROM_WORKSPACE", true,
+            "ENABLE_SPLIT_FROM_WORKSPACE", ENABLED,
             "Enable initiating split screen from workspace.");
 
     public static final BooleanFlag ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS =
-            getDebugFlag(270394122, "ENABLE_SPLIT_FROM_FULLSCREEN_SHORTCUT", true,
-                    "Enable splitting from fullscreen app with keyboard shortcuts");
+            getDebugFlag(270394122, "ENABLE_SPLIT_FROM_FULLSCREEN_SHORTCUT", ENABLED,
+            "Enable splitting from fullscreen app with keyboard shortcuts");
 
     public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE = getDebugFlag(
-            270393453, "ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", false,
+            270393453, "ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", DISABLED,
             "Enable initiating split screen from workspace to workspace.");
 
     public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(270393455,
-            "ENABLE_NEW_MIGRATION_LOGIC", true,
+            "ENABLE_NEW_MIGRATION_LOGIC", ENABLED,
             "Enable the new grid migration logic, keeping pages when src < dest");
 
     public static final BooleanFlag ENABLE_WIDGET_HOST_IN_BACKGROUND = getDebugFlag(270394384,
-            "ENABLE_WIDGET_HOST_IN_BACKGROUND", true,
+            "ENABLE_WIDGET_HOST_IN_BACKGROUND", ENABLED,
             "Enable background widget updates listening for widget holder");
 
     public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = getReleaseFlag(270394223,
-            "ENABLE_ONE_SEARCH_MOTION", true, "Enables animations in OneSearch.");
+            "ENABLE_ONE_SEARCH_MOTION", ENABLED, "Enables animations in OneSearch.");
 
     public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = getReleaseFlag(
-            270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", false,
+            270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", DISABLED,
             "Enable option to replace decorator-based search result backgrounds with drawables");
 
     public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = getReleaseFlag(
-            270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", false,
+            270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", DISABLED,
             "Enable option to launch search results using the new view container transitions");
 
     public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag(
-            270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", true,
+            270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", ENABLED,
             "Enable option to show keyboard when going to all-apps");
 
     public static final BooleanFlag USE_LOCAL_ICON_OVERRIDES = getDebugFlag(270394973,
-            "USE_LOCAL_ICON_OVERRIDES", true,
+            "USE_LOCAL_ICON_OVERRIDES", ENABLED,
             "Use inbuilt monochrome icons if app doesn't provide one");
 
     public static final BooleanFlag ENABLE_DISMISS_PREDICTION_UNDO = getDebugFlag(270394476,
-            "ENABLE_DISMISS_PREDICTION_UNDO", false,
+            "ENABLE_DISMISS_PREDICTION_UNDO", DISABLED,
             "Show an 'Undo' snackbar when users dismiss a predicted hotseat item");
 
     public static final BooleanFlag ENABLE_CACHED_WIDGET = getDebugFlag(270395008,
-            "ENABLE_CACHED_WIDGET", true,
+            "ENABLE_CACHED_WIDGET", ENABLED,
             "Show previously cached widgets as opposed to deferred widget where available");
 
     public static final BooleanFlag USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES = getDebugFlag(270395010,
-            "USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", false,
+            "USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", DISABLED,
             "Use local overrides for search request timeout");
 
     public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(270395171,
-            "CONTINUOUS_VIEW_TREE_CAPTURE", true, "Capture View tree every frame");
+            "CONTINUOUS_VIEW_TREE_CAPTURE", ENABLED, "Capture View tree every frame");
 
-    public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(
-            270395140, "SECONDARY_DRAG_N_DROP_TO_PIN", false,
+    public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(270395140,
+            "SECONDARY_DRAG_N_DROP_TO_PIN", DISABLED,
             "Enable dragging and dropping to pin apps within secondary display");
 
     public static final BooleanFlag FOLDABLE_WORKSPACE_REORDER = getDebugFlag(270395070,
-            "FOLDABLE_WORKSPACE_REORDER", false,
+            "FOLDABLE_WORKSPACE_REORDER", DISABLED,
             "In foldables, when reordering the icons and widgets, is now going to use both sides");
 
     public static final BooleanFlag ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH = getDebugFlag(270395073,
-            "ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH", false,
+            "ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH", DISABLED,
             "Allow bottom sheet depth to be smaller than 1 for multi-display devices.");
 
-    public static final BooleanFlag SCROLL_TOP_TO_RESET = getReleaseFlag(
-            270395177, "SCROLL_TOP_TO_RESET", true,
+    public static final BooleanFlag SCROLL_TOP_TO_RESET = getReleaseFlag(270395177,
+            "SCROLL_TOP_TO_RESET", ENABLED,
             "Bring up IME and focus on input when scroll to top if 'Always show keyboard'"
                     + " is enabled or in prefix state");
 
     public static final BooleanFlag ENABLE_MATERIAL_U_POPUP = getDebugFlag(270395516,
-            "ENABLE_MATERIAL_U_POPUP", true, "Switch popup UX to use material U");
+            "ENABLE_MATERIAL_U_POPUP", ENABLED, "Switch popup UX to use material U");
 
     public static final BooleanFlag ENABLE_SEARCH_UNINSTALLED_APPS = getReleaseFlag(270395269,
-            "ENABLE_SEARCH_UNINSTALLED_APPS", false, "Search uninstalled app results.");
+            "ENABLE_SEARCH_UNINSTALLED_APPS", DISABLED, "Search uninstalled app results.");
 
     public static final BooleanFlag SHOW_HOME_GARDENING = getDebugFlag(270395183,
-            "SHOW_HOME_GARDENING", false,
-            "Show the new home gardening mode");
+            "SHOW_HOME_GARDENING", DISABLED, "Show the new home gardening mode");
 
     public static final BooleanFlag HOME_GARDENING_WORKSPACE_BUTTONS = getDebugFlag(270395133,
-            "HOME_GARDENING_WORKSPACE_BUTTONS", false,
+            "HOME_GARDENING_WORKSPACE_BUTTONS", DISABLED,
             "Change workspace edit buttons to reflect home gardening");
 
     public static final BooleanFlag ENABLE_DOWNLOAD_APP_UX_V2 = getReleaseFlag(270395134,
-            "ENABLE_DOWNLOAD_APP_UX_V2", true, "Updates the download app UX"
+            "ENABLE_DOWNLOAD_APP_UX_V2", ENABLED, "Updates the download app UX"
                     + " to have better visuals");
 
     public static final BooleanFlag ENABLE_DOWNLOAD_APP_UX_V3 = getDebugFlag(270395186,
-            "ENABLE_DOWNLOAD_APP_UX_V3", false, "Updates the download app UX"
+            "ENABLE_DOWNLOAD_APP_UX_V3", DISABLED, "Updates the download app UX"
                     + " to have better visuals, improve contrast, and color");
 
     public static final BooleanFlag FORCE_PERSISTENT_TASKBAR = getDebugFlag(270395077,
-            "FORCE_PERSISTENT_TASKBAR", false, "Forces taskbar to be persistent, even in gesture"
+            "FORCE_PERSISTENT_TASKBAR", DISABLED, "Forces taskbar to be persistent, even in gesture"
                     + " nav mode and when transient taskbar is enabled.");
 
     public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(270395274,
-            "FOLDABLE_SINGLE_PAGE", true,
-            "Use a single page for the workspace");
+            "FOLDABLE_SINGLE_PAGE", ENABLED, "Use a single page for the workspace");
 
     public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(270395798,
-            "ENABLE_TRANSIENT_TASKBAR", true, "Enables transient taskbar.");
+            "ENABLE_TRANSIENT_TASKBAR", ENABLED, "Enables transient taskbar.");
 
     public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401,
-            "ENABLE_TRACKPAD_GESTURE", true, "Enables trackpad gesture.");
+            "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture.");
 
     public static final BooleanFlag ENABLE_ICON_IN_TEXT_HEADER = getDebugFlag(270395143,
-            "ENABLE_ICON_IN_TEXT_HEADER", false, "Show icon in textheader");
+            "ENABLE_ICON_IN_TEXT_HEADER", DISABLED, "Show icon in textheader");
 
     public static final BooleanFlag ENABLE_APP_ICON_FOR_INLINE_SHORTCUTS = getDebugFlag(270395087,
-            "ENABLE_APP_ICON_IN_INLINE_SHORTCUTS", false, "Show app icon for inline shortcut");
+            "ENABLE_APP_ICON_IN_INLINE_SHORTCUTS", DISABLED, "Show app icon for inline shortcut");
 
     public static final BooleanFlag SHOW_DOT_PAGINATION = getDebugFlag(270395278,
-            "SHOW_DOT_PAGINATION", true, "Enable showing dot pagination in workspace");
+            "SHOW_DOT_PAGINATION", ENABLED, "Enable showing dot pagination in workspace");
 
     public static final BooleanFlag LARGE_SCREEN_WIDGET_PICKER = getDebugFlag(270395809,
-            "LARGE_SCREEN_WIDGET_PICKER", true, "Enable new widget picker that takes "
+            "LARGE_SCREEN_WIDGET_PICKER", ENABLED, "Enable new widget picker that takes "
                     + "advantage of large screen format");
 
     public static final BooleanFlag ENABLE_NEW_GESTURE_NAV_TUTORIAL = getDebugFlag(270396257,
-            "ENABLE_NEW_GESTURE_NAV_TUTORIAL", true,
+            "ENABLE_NEW_GESTURE_NAV_TUTORIAL", ENABLED,
             "Enable the redesigned gesture navigation tutorial");
 
     public static final BooleanFlag ENABLE_LAUNCH_FROM_STAGED_APP = getDebugFlag(270395567,
-            "ENABLE_LAUNCH_FROM_STAGED_APP", true,
-            "Enable the ability to tap a staged app during split select to launch it in full screen"
-    );
+            "ENABLE_LAUNCH_FROM_STAGED_APP", ENABLED,
+            "Enable the ability to tap a staged app during split select to launch it in full "
+                    + "screen");
 
     public static final BooleanFlag ENABLE_PREMIUM_HAPTICS_ALL_APPS = getDebugFlag(270396358,
-            "ENABLE_PREMIUM_HAPTICS_ALL_APPS", false,
+            "ENABLE_PREMIUM_HAPTICS_ALL_APPS", DISABLED,
             "Enables haptics opening/closing All apps");
 
     public static final BooleanFlag ENABLE_FORCED_MONO_ICON = getDebugFlag(270396209,
-            "ENABLE_FORCED_MONO_ICON", false,
-            "Enable the ability to generate monochromatic icons, if it is not provided by the app"
-    );
+            "ENABLE_FORCED_MONO_ICON", DISABLED,
+            "Enable the ability to generate monochromatic icons, if it is not provided by the app");
 
     public static final BooleanFlag ENABLE_TASKBAR_EDU_TOOLTIP = getDebugFlag(270396268,
-            "ENABLE_TASKBAR_EDU_TOOLTIP", true,
+            "ENABLE_TASKBAR_EDU_TOOLTIP", ENABLED,
             "Enable the tooltip version of the Taskbar education flow.");
 
     public static final BooleanFlag ENABLE_MULTI_INSTANCE = getDebugFlag(270396680,
-            "ENABLE_MULTI_INSTANCE", false,
+            "ENABLE_MULTI_INSTANCE", DISABLED,
             "Enables creation and filtering of multiple task instances in overview");
 
     public static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(270396583,
-            "ENABLE_TASKBAR_PINNING", false,
+            "ENABLE_TASKBAR_PINNING", DISABLED,
             "Enables taskbar pinning to allow user to switch between transient and persistent "
                     + "taskbar flavors");
 
     public static final BooleanFlag ENABLE_WORKSPACE_LOADING_OPTIMIZATION = getDebugFlag(251502424,
-            "ENABLE_WORKSPACE_LOADING_OPTIMIZATION", false, "load the current workspace screen "
-                    + "visible to the user before the rest rather than loading all of them at once."
-    );
+            "ENABLE_WORKSPACE_LOADING_OPTIMIZATION", DISABLED,
+            "load the current workspace screen visible to the user before the rest rather than "
+                    + "loading all of them at once.");
 
     public static final BooleanFlag ENABLE_GRID_ONLY_OVERVIEW = getDebugFlag(270397206,
-            "ENABLE_GRID_ONLY_OVERVIEW", false,
+            "ENABLE_GRID_ONLY_OVERVIEW", DISABLED,
             "Enable a grid-only overview without a focused task.");
 
     public static final BooleanFlag RECEIVE_UNFOLD_EVENTS_FROM_SYSUI = getDebugFlag(270397209,
-            "RECEIVE_UNFOLD_EVENTS_FROM_SYSUI", true,
+            "RECEIVE_UNFOLD_EVENTS_FROM_SYSUI", ENABLED,
             "Enables receiving unfold animation events from sysui instead of calculating "
                     + "them in launcher process using hinge sensor values.");
 
     public static final BooleanFlag ENABLE_KEYBOARD_QUICK_SWITCH = getDebugFlag(270396844,
-            "ENABLE_KEYBOARD_QUICK_SWITCH", true,
-            "Enables keyboard quick switching");
+            "ENABLE_KEYBOARD_QUICK_SWITCH", ENABLED, "Enables keyboard quick switching");
 
     public static final BooleanFlag ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER = getDebugFlag(266177840,
-            "ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER", false,
+            "ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER", DISABLED,
             "Removes clone apps from the work profile tab.");
 
     public static final BooleanFlag ENABLE_APP_PAIRS = getDebugFlag(274189428,
-            "ENABLE_APP_PAIRS", false,
+            "ENABLE_APP_PAIRS", DISABLED,
             "Enables the ability to create and save app pairs on the Home screen for easy"
                     + " split screen launching.");
 
@@ -437,4 +428,13 @@
             return sIntReader.applyAsInt(this);
         }
     }
+
+    /**
+     * Enabled state for a flag
+     */
+    public enum FlagState {
+        ENABLED,
+        DISABLED,
+        TEAMFOOD    // Enabled in team food
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index d43731b..9fe3c0b 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -45,7 +45,7 @@
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.pageindicators.LauncherDotsPageIndicator;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
 import com.android.launcher3.util.Thunk;
@@ -60,7 +60,7 @@
 import java.util.function.ToIntFunction;
 import java.util.stream.Collectors;
 
-public class FolderPagedView extends PagedView<PageIndicatorDots> implements ClipPathView {
+public class FolderPagedView extends PagedView<LauncherDotsPageIndicator> implements ClipPathView {
 
     private static final String TAG = "FolderPagedView";
 
diff --git a/src/com/android/launcher3/pageindicators/LauncherDotsPageIndicator.java b/src/com/android/launcher3/pageindicators/LauncherDotsPageIndicator.java
new file mode 100644
index 0000000..8a21d13
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/LauncherDotsPageIndicator.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.pageindicators;
+
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
+
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Alarm;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.util.MultiValueAlpha;
+
+/**
+ * Extension of {@link PageIndicatorDots} with Launcher specific page-indicator functionality
+ */
+public class LauncherDotsPageIndicator extends PageIndicatorDots
+        implements Insettable, PageIndicator {
+
+    private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
+    private static final int PAGINATION_FADE_IN_DURATION = 83;
+    private static final int PAGINATION_FADE_OUT_DURATION = 167;
+
+    private static final int INDEX_VIEW_ALPHA = 0;
+    private static final int INDEX_AUTO_HIDE = 1;
+    private static final int ALPHA_CHANNEL_COUNT = 2;
+
+    private final Alarm mAutoHideAlarm;
+    private final MultiValueAlpha mMultiValueAlpha;
+
+    private @Nullable ObjectAnimator mAlphaAnimator;
+    private boolean mShouldAutoHide;
+    private float mTargetAutoHideAlpha;
+
+    private boolean mIsSettled = true;
+    private int mTotalScroll;
+
+    public LauncherDotsPageIndicator(Context context) {
+        this(context, null);
+    }
+
+    public LauncherDotsPageIndicator(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public LauncherDotsPageIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
+        mMultiValueAlpha.setUpdateVisibility(true);
+
+        mTargetAutoHideAlpha = mMultiValueAlpha.get(INDEX_AUTO_HIDE).getValue();
+
+        mAutoHideAlarm = new Alarm();
+        mAutoHideAlarm.setOnAlarmListener(a -> animatePaginationToAlpha(0));
+    }
+
+    @Override
+    public void setScroll(int currentScroll, int totalScroll) {
+        mTotalScroll = totalScroll;
+        super.setScroll(currentScroll, totalScroll);
+    }
+
+    @Override
+    public void setShouldAutoHide(boolean shouldAutoHide) {
+        mShouldAutoHide = shouldAutoHide;
+        mAutoHideAlarm.cancelAlarm();
+        if (!mIsSettled || !mShouldAutoHide) {
+            animatePaginationToAlpha(1);
+        } else {
+            mAutoHideAlarm.setAlarm(PAGINATION_FADE_DELAY);
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mShouldAutoHide && mTotalScroll == 0) {
+            return;
+        }
+        super.onDraw(canvas);
+    }
+
+    @Override
+    public void setActiveMarker(int activePage) {
+        super.setActiveMarker(activePage);
+    }
+
+    @Override
+    public void setMarkersCount(int numMarkers) {
+        super.setMarkersCount(numMarkers);
+    }
+
+    @Override
+    public void pauseAnimations() {
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.pause();
+        }
+    }
+
+    @Override
+    public void skipAnimationsToEnd() {
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.end();
+        }
+    }
+
+    @Override
+    protected void onAnimationStateChanged(boolean isSettled) {
+        mIsSettled = isSettled;
+        if (!mShouldAutoHide) {
+            return;
+        }
+        mAutoHideAlarm.cancelAlarm();
+        if (isSettled) {
+            mAutoHideAlarm.setAlarm(PAGINATION_FADE_DELAY);
+        } else {
+            animatePaginationToAlpha(1f);
+        }
+    }
+
+    private void animatePaginationToAlpha(float targetAlpha) {
+        if (mTargetAutoHideAlpha == targetAlpha) {
+            // Ignore the new animation if it is going to the same alpha as the current animation.
+            return;
+        }
+
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.cancel();
+        }
+        mAlphaAnimator = ObjectAnimator.ofFloat(mMultiValueAlpha.get(INDEX_AUTO_HIDE),
+                MULTI_PROPERTY_VALUE, targetAlpha);
+        // If we are animating to decrease the alpha, then it's a fade out animation
+        // whereas if we are animating to increase the alpha, it's a fade in animation.
+        mAlphaAnimator.setDuration(targetAlpha == 0
+                ? PAGINATION_FADE_OUT_DURATION
+                : PAGINATION_FADE_IN_DURATION);
+        mAlphaAnimator.addListener(forEndCallback(() -> mAlphaAnimator = null));
+        mAlphaAnimator.start();
+        mTargetAutoHideAlpha = targetAlpha;
+    }
+
+
+    @Override
+    public void stopAllAnimations() {
+        super.stopAllAnimations();
+    }
+
+    @Override
+    public void prepareEntryAnimation() {
+        super.prepareEntryAnimation();
+    }
+
+    @Override
+    public void playEntryAnimation() {
+        super.playEntryAnimation();
+    }
+
+    /**
+     * We need to override setInsets to prevent InsettableFrameLayout from applying different
+     * margins on the pagination.
+     */
+    @Override
+    public void setInsets(Rect insets) {
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    @Override
+    public void setAlpha(PropertySetter setter, float alpha, TimeInterpolator interpolator) {
+        setter.setFloat(mMultiValueAlpha.get(INDEX_VIEW_ALPHA),
+                MULTI_PROPERTY_VALUE, alpha, interpolator);
+    }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 570d6ff..193f50d 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -15,6 +15,11 @@
  */
 package com.android.launcher3.pageindicators;
 
+import android.animation.TimeInterpolator;
+import android.view.View;
+
+import com.android.launcher3.anim.PropertySetter;
+
 /**
  * Base class for a page indicator.
  */
@@ -48,9 +53,9 @@
     }
 
     /**
-     * Sets the paint color.
+     * Sets the provided alpha on the pageIndicator
      */
-    default void setPaintColor(int color) {
-        // No-op by default
+    default void setAlpha(PropertySetter setter, float alpha, TimeInterpolator interpolator) {
+        setter.setViewAlpha((View) this, alpha, interpolator);
     }
 }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index b2c64b3..95452b9 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -25,42 +25,30 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
-import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
-import android.util.IntProperty;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewOutlineProvider;
 import android.view.animation.Interpolator;
 import android.view.animation.OvershootInterpolator;
 
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Themes;
 
 /**
  * {@link PageIndicator} which shows dots per page. The active page is shown with the current
  * accent color.
  */
-public class PageIndicatorDots extends View implements Insettable, PageIndicator {
+public class PageIndicatorDots extends View {
 
     private static final float SHIFT_PER_ANIMATION = 0.5f;
     private static final float SHIFT_THRESHOLD = 0.1f;
     private static final long ANIMATION_DURATION = 150;
-    private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
-    private static final int PAGINATION_FADE_IN_DURATION = 83;
-    private static final int PAGINATION_FADE_OUT_DURATION = 167;
 
     private static final int ENTER_ANIMATION_START_DELAY = 300;
     private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
@@ -70,9 +58,6 @@
     private static final int DOT_ALPHA = 128;
     private static final float DOT_ALPHA_FRACTION = 0.5f;
     private static final int DOT_GAP_FACTOR = SHOW_DOT_PAGINATION.get() ? 4 : 3;
-    private static final int VISIBLE_ALPHA = 255;
-    private static final int INVISIBLE_ALPHA = 0;
-    private Paint mPaginationPaint;
 
     // This value approximately overshoots to 1.5 times the original size.
     private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
@@ -94,30 +79,14 @@
                 }
     };
 
-    private static final IntProperty<PageIndicatorDots> PAGINATION_ALPHA =
-            new IntProperty<PageIndicatorDots>("pagination_alpha") {
-        @Override
-        public Integer get(PageIndicatorDots obj) {
-            return obj.mPaginationPaint.getAlpha();
-        }
-
-        @Override
-        public void setValue(PageIndicatorDots obj, int alpha) {
-            obj.mPaginationPaint.setAlpha(alpha);
-            obj.invalidate();
-        }
-    };
-
-    private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper());
     private final float mDotRadius;
     private final float mCircleGap;
     private final boolean mIsRtl;
 
+    private final Paint mPaginationPaint;
+
     private int mNumPages;
     private int mActivePage;
-    private int mTotalScroll;
-    private boolean mShouldAutoHide;
-    private int mToAlpha;
 
     /**
      * The current position of the active dot including the animation progress.
@@ -131,13 +100,9 @@
     private float mCurrentPosition;
     private float mFinalPosition;
     private ObjectAnimator mAnimator;
-    private @Nullable ObjectAnimator mAlphaAnimator;
 
     private float[] mEntryAnimationRadiusFactors;
 
-    private final Runnable mHidePaginationRunnable =
-            () -> animatePaginationToAlpha(INVISIBLE_ALPHA);
-
     public PageIndicatorDots(Context context) {
         this(context, null);
     }
@@ -151,37 +116,34 @@
 
         mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mPaginationPaint.setStyle(Style.FILL);
-        mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
+
+        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PagedIndicator);
+        mPaginationPaint.setColor(ta.getColor(R.styleable.PagedIndicator_indicatorDotColor, 0));
+        ta.recycle();
+
         mDotRadius = (SHOW_DOT_PAGINATION.get()
                 ? getResources().getDimension(R.dimen.page_indicator_dot_size_v2)
                 : getResources().getDimension(R.dimen.page_indicator_dot_size))
                 / 2;
         mCircleGap = DOT_GAP_FACTOR * mDotRadius;
         setOutlineProvider(new MyOutlineProver());
-        mIsRtl = Utilities.isRtl(getResources());
+        mIsRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
     }
 
-    @Override
     public void setScroll(int currentScroll, int totalScroll) {
         if (SHOW_DOT_PAGINATION.get() && mActivePage != 0 && currentScroll == 0) {
             CURRENT_POSITION.set(this, (float) mActivePage);
             return;
         }
 
-        if (mNumPages <= 1) {
+        if (mNumPages <= 1 || totalScroll == 0) {
             return;
         }
 
-        if (mShouldAutoHide) {
-            animatePaginationToAlpha(VISIBLE_ALPHA);
-        }
-
         if (mIsRtl) {
             currentScroll = totalScroll - currentScroll;
         }
 
-        mTotalScroll = totalScroll;
-
         int scrollPerPage = totalScroll / (mNumPages - 1);
         int pageToLeft = scrollPerPage == 0 ? 0 : currentScroll / scrollPerPage;
         int pageToLeftScroll = pageToLeft * scrollPerPage;
@@ -191,87 +153,12 @@
         if (currentScroll < pageToLeftScroll + scrollThreshold) {
             // scroll is within the left page's threshold
             animateToPosition(pageToLeft);
-            if (mShouldAutoHide) {
-                hideAfterDelay();
-            }
         } else if (currentScroll > pageToRightScroll - scrollThreshold) {
             // scroll is far enough from left page to go to the right page
             animateToPosition(pageToLeft + 1);
-            if (mShouldAutoHide) {
-                hideAfterDelay();
-            }
         } else {
             // scroll is between left and right page
             animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
-            if (mShouldAutoHide) {
-                mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
-            }
-        }
-    }
-
-    @Override
-    public void setShouldAutoHide(boolean shouldAutoHide) {
-        mShouldAutoHide = shouldAutoHide && SHOW_DOT_PAGINATION.get();
-        if (shouldAutoHide && mPaginationPaint.getAlpha() > INVISIBLE_ALPHA) {
-            hideAfterDelay();
-        } else if (!shouldAutoHide) {
-            mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
-        }
-    }
-
-    @Override
-    public void setPaintColor(int color) {
-        mPaginationPaint.setColor(color);
-    }
-
-    private void hideAfterDelay() {
-        mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
-        mDelayedPaginationFadeHandler.postDelayed(mHidePaginationRunnable, PAGINATION_FADE_DELAY);
-    }
-
-    private void animatePaginationToAlpha(int alpha) {
-        if (alpha == mToAlpha) {
-            // Ignore the new animation if it is going to the same alpha as the current animation.
-            return;
-        }
-
-        if (mAlphaAnimator != null) {
-            mAlphaAnimator.cancel();
-        }
-        mAlphaAnimator = ObjectAnimator.ofInt(this, PAGINATION_ALPHA,
-                alpha);
-        // If we are animating to decrease the alpha, then it's a fade out animation
-        // whereas if we are animating to increase the alpha, it's a fade in animation.
-        mAlphaAnimator.setDuration(alpha < mToAlpha
-                ? PAGINATION_FADE_OUT_DURATION
-                : PAGINATION_FADE_IN_DURATION);
-        mAlphaAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAlphaAnimator = null;
-            }
-        });
-        mAlphaAnimator.start();
-        mToAlpha = alpha;
-    }
-
-    /**
-     * Pauses all currently running animations.
-     */
-    @Override
-    public void pauseAnimations() {
-        if (mAlphaAnimator != null) {
-            mAlphaAnimator.pause();
-        }
-    }
-
-    /**
-     * Force-ends all currently running or paused animations.
-     */
-    @Override
-    public void skipAnimationsToEnd() {
-        if (mAlphaAnimator != null) {
-            mAlphaAnimator.end();
         }
     }
 
@@ -281,15 +168,25 @@
             mCurrentPosition = mFinalPosition;
         }
         if (mAnimator == null && Float.compare(mCurrentPosition, mFinalPosition) != 0) {
+            onAnimationStateChanged(false);
             float positionForThisAnim = mCurrentPosition > mFinalPosition ?
                     mCurrentPosition - SHIFT_PER_ANIMATION : mCurrentPosition + SHIFT_PER_ANIMATION;
             mAnimator = ObjectAnimator.ofFloat(this, CURRENT_POSITION, positionForThisAnim);
             mAnimator.addListener(new AnimationCycleListener());
             mAnimator.setDuration(ANIMATION_DURATION);
             mAnimator.start();
+        } else if (mAnimator == null) {
+            // The state is only settled if the indicator lands on a int value
+            onAnimationStateChanged(Float.compare(Math.round(mFinalPosition), mFinalPosition) == 0);
         }
     }
 
+    /**
+     * Called when the animation state of the page indicator changes.
+     * @param isSettled true if the page indicator has settled at its final position
+     */
+    protected void onAnimationStateChanged(boolean isSettled) { }
+
     public void stopAllAnimations() {
         if (mAnimator != null) {
             mAnimator.cancel();
@@ -345,14 +242,10 @@
         animSet.start();
     }
 
-    @Override
     public void setActiveMarker(int activePage) {
-        if (mActivePage != activePage) {
-            mActivePage = activePage;
-        }
+        mActivePage = activePage;
     }
 
-    @Override
     public void setMarkersCount(int numMarkers) {
         mNumPages = numMarkers;
         requestLayout();
@@ -374,11 +267,6 @@
             return;
         }
 
-        if (mShouldAutoHide && mTotalScroll == 0) {
-            mPaginationPaint.setAlpha(INVISIBLE_ALPHA);
-            return;
-        }
-
         // Draw all page indicators;
         float circleGap = mCircleGap;
         float startX = (getWidth() - (mNumPages * circleGap) + mDotRadius) / 2;
@@ -480,20 +368,9 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             if (!mCancelled) {
-                if (mShouldAutoHide && SHOW_DOT_PAGINATION.get()) {
-                    hideAfterDelay();
-                }
                 mAnimator = null;
                 animateToPosition(mFinalPosition);
             }
         }
     }
-
-    /**
-     * We need to override setInsets to prevent InsettableFrameLayout from applying different
-     * margins on the pagination.
-     */
-    @Override
-    public void setInsets(Rect insets) {
-    }
 }
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 8c5e782..1ae43d0 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -200,8 +200,8 @@
         /** Keep in sync w/ ActivityTaskManager#INVALID_TASK_ID (unreference-able) */
         private static final int INVALID_TASK_ID = -1;
 
-        public final View view;
-        public final Drawable drawable;
+        private View view;
+        private Drawable drawable;
         public final Intent intent;
         public final SplitPositionOption position;
         public final ItemInfo itemInfo;
@@ -224,5 +224,13 @@
             this.itemInfo = itemInfo;
             this.splitEvent = splitEvent;
         }
+
+        public Drawable getDrawable() {
+            return drawable;
+        }
+
+        public View getView() {
+            return view;
+        }
     }
 }
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index a941833..c0b24fa 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -333,6 +333,7 @@
         }
     }
 
+    @Override
     public void onDraw(Canvas canvas) {
         if (mThumbOffsetY < 0 || mRv == null) {
             return;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 5293c3d..2325376 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -110,6 +110,7 @@
             entry -> !mCurrentUser.equals(entry.mPkgItem.user)
                     && !mUserManagerState.isUserQuiet(entry.mPkgItem.user);
     protected final boolean mHasWorkProfile;
+    protected boolean mHasRecommendedWidgets;
     protected final SparseArray<AdapterHolder> mAdapters = new SparseArray();
     @Nullable private ArrowTipView mLatestEducationalTip;
     private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
@@ -537,7 +538,6 @@
     public void onSearchResults(List<WidgetsListBaseEntry> entries) {
         mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
         updateRecyclerViewVisibility(mAdapters.get(AdapterHolder.SEARCH));
-        mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
     }
 
     protected void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
@@ -574,7 +574,8 @@
         }
         List<WidgetItem> recommendedWidgets =
                 mActivityContext.getPopupDataProvider().getRecommendedWidgets();
-        if (recommendedWidgets.size() > 0) {
+        mHasRecommendedWidgets = recommendedWidgets.size() > 0;
+        if (mHasRecommendedWidgets) {
             float noWidgetsViewHeight = 0;
             if (mIsNoWidgetsViewNeeded) {
                 // Make sure recommended section leaves enough space for noWidgetsView.
@@ -603,14 +604,10 @@
             mRecommendedWidgetsTable.setRecommendedWidgets(
                     recommendedWidgetsInTable, maxTableHeight);
         } else {
-            hideRecommendations();
+            mRecommendedWidgetsTable.setVisibility(GONE);
         }
     }
 
-    protected void hideRecommendations() {
-        mRecommendedWidgetsTable.setVisibility(GONE);
-    }
-
     protected float getMaxTableHeight(float noWidgetsViewHeight) {
         return (mContent.getMeasuredHeight()
                 - mTabsHeight - getHeaderViewHeight()
@@ -896,7 +893,7 @@
         final WidgetsListAdapter mWidgetsListAdapter;
         private final DefaultItemAnimator mWidgetsListItemAnimator;
 
-        private WidgetsRecyclerView mWidgetsRecyclerView;
+        WidgetsRecyclerView mWidgetsRecyclerView;
 
         AdapterHolder(int adapterType) {
             mAdapterType = adapterType;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 2199473..46aae9d 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -54,6 +54,7 @@
 
     private ScrollView mRightPaneScrollView;
     private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
+    private int mActivePage = -1;
 
     private final ViewOutlineProvider mViewOutlineProviderRightPane = new ViewOutlineProvider() {
         @Override
@@ -107,7 +108,6 @@
         mRightPaneScrollView = mContent.findViewById(R.id.right_pane_scroll_view);
         mRightPaneScrollView.setOverScrollMode(View.OVER_SCROLL_NEVER);
 
-        setupSuggestedWidgets(layoutInflater);
         onRecommendedWidgetsBound();
         onWidgetsBound();
         setUpEducationViewsIfNeeded();
@@ -117,9 +117,13 @@
     }
 
     @Override
-    protected void hideRecommendations() {
-        super.hideRecommendations();
-        mSuggestedWidgetsContainer.setVisibility(GONE);
+    public void onRecommendedWidgetsBound() {
+        super.onRecommendedWidgetsBound();
+
+        if (mSuggestedWidgetsContainer == null && mHasRecommendedWidgets) {
+            setupSuggestedWidgets(LayoutInflater.from(getContext()));
+            mSuggestedWidgetsHeader.callOnClick();
+        }
     }
 
     private void setupSuggestedWidgets(LayoutInflater layoutInflater) {
@@ -168,13 +172,21 @@
 
     @Override
     public void onActivePageChanged(int currentActivePage) {
-        // if the current active page changes to personal or work we set suggestions
-        // to be the selected widget
-        if (currentActivePage == PERSONAL_TAB || currentActivePage == WORK_TAB) {
-            mSuggestedWidgetsHeader.callOnClick();
+        super.onActivePageChanged(currentActivePage);
+
+        // If active page didn't change then we don't want to update the header.
+        if (mActivePage == currentActivePage) {
+            return;
         }
 
-        super.onActivePageChanged(currentActivePage);
+        mActivePage = currentActivePage;
+
+        if (mSuggestedWidgetsHeader == null) {
+            mAdapters.get(currentActivePage).mWidgetsListAdapter.selectFirstHeaderEntry();
+            mAdapters.get(currentActivePage).mWidgetsRecyclerView.scrollToTop();
+        } else if (currentActivePage == PERSONAL_TAB || currentActivePage == WORK_TAB) {
+            mSuggestedWidgetsHeader.callOnClick();
+        }
     }
 
     @Override
@@ -188,15 +200,10 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mSuggestedWidgetsContainer.setVisibility(VISIBLE);
-    }
-
-    @Override
     public void onSearchResults(List<WidgetsListBaseEntry> entries) {
         super.onSearchResults(entries);
         mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.selectFirstHeaderEntry();
+        mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
     }
 
     @Override
@@ -208,13 +215,19 @@
 
     @Override
     protected void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
-        if (isInSearchMode) {
-            mSuggestedWidgetsContainer.setVisibility(GONE);
-        } else {
-            mSuggestedWidgetsContainer.setVisibility(VISIBLE);
-            mSuggestedWidgetsHeader.callOnClick();
-        }
         super.setViewVisibilityBasedOnSearch(isInSearchMode);
+
+        if (mSuggestedWidgetsHeader != null && mSuggestedWidgetsContainer != null) {
+            if (!isInSearchMode) {
+                mSuggestedWidgetsContainer.setVisibility(VISIBLE);
+                mSuggestedWidgetsHeader.callOnClick();
+            } else {
+                mSuggestedWidgetsContainer.setVisibility(GONE);
+            }
+        } else if (!isInSearchMode) {
+            mAdapters.get(mActivePage).mWidgetsListAdapter.selectFirstHeaderEntry();
+        }
+
     }
 
     @Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java
index 4463adc..eb0494e 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/flags/FlagsFactory.java
@@ -16,7 +16,10 @@
 
 package com.android.launcher3.uioverrides.flags;
 
+import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED;
+
 import com.android.launcher3.config.FeatureFlags.BooleanFlag;
+import com.android.launcher3.config.FeatureFlags.FlagState;
 import com.android.launcher3.config.FeatureFlags.IntFlag;
 
 import java.io.PrintWriter;
@@ -31,16 +34,16 @@
      * Creates a new debug flag
      */
     public static BooleanFlag getDebugFlag(
-            int bugId, String key, boolean defaultValue, String description) {
-        return new BooleanFlag(defaultValue);
+            int bugId, String key, FlagState flagState, String description) {
+        return new BooleanFlag(flagState == ENABLED);
     }
 
     /**
      * Creates a new debug flag
      */
     public static BooleanFlag getReleaseFlag(
-            int bugId, String key, boolean defaultValueInCode, String description) {
-        return new BooleanFlag(defaultValueInCode);
+            int bugId, String key, FlagState flagState, String description) {
+        return new BooleanFlag(flagState == ENABLED);
     }
 
     /**
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 199653b..54da7de 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -19,6 +19,8 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
 import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -37,6 +39,7 @@
 import com.android.launcher3.allapps.WorkProfileManager;
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.rule.TestStabilityRule.Stability;
 
 import org.junit.After;
 import org.junit.Before;
@@ -113,6 +116,7 @@
     }
 
     @Test
+    @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/243688989
     public void workTabExists() {
         assumeTrue(mWorkProfileSetupSuccessful);
         waitForWorkTabSetup();
@@ -173,6 +177,7 @@
     }
 
     @Test
+    @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/243688989
     public void testEdu() {
         assumeTrue(mWorkProfileSetupSuccessful);
         waitForWorkTabSetup();
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 2d4d2cd..885707c 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -147,6 +147,9 @@
                                 getAppsListRecyclerBottomPadding());
                         verifyActiveContainer();
                         final int newScroll = getAllAppsScroll();
+                        LauncherInstrumentation.log(
+                                String.format("tryGetAppIcon: scrolled from %d to %d", scroll,
+                                        newScroll));
                         mLauncher.assertTrue(
                                 "Scrolled in a wrong direction in AllApps: from " + scroll + " to "
                                         + newScroll, newScroll >= scroll);
@@ -259,8 +262,7 @@
     }
 
     private int getAllAppsScroll() {
-        return mLauncher.getTestInfo(
-                TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
+        return mLauncher.getTestInfo(TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
                 .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
index 8cdc8a0..54be3c3 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -49,4 +49,10 @@
             }
         }
     }
+
+    /** Returns true if an item matching the given string is present in the menu. */
+    public boolean hasMenuItem(String expectedMenuItemText) {
+        UiObject2 menuItem = mLauncher.findObjectInContainer(mMenu, By.text(expectedMenuItemText));
+        return menuItem != null;
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index c1c26ec..79b54ba 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -182,12 +182,6 @@
             UiObject2 widgetListView = verifyActiveContainer();
             UiObject2 header = mLauncher.waitForObjectInContainer(widgetListView,
                     headerSelector);
-            // If we are in a tablet in landscape mode then we will have a two pane view and we use
-            // the right pane to display the widgets table.
-            UiObject2 rightPane = mLauncher.findObjectInContainer(
-                    widgetPicker,
-                    widgetsContainerSelector);
-
             // If a header is barely visible in the bottom edge of the screen, its height could be
             // too small for a scroll gesture. Since all header should have roughly the same height,
             // let's pick the max height we have seen so far.
@@ -209,6 +203,12 @@
                     mLauncher.clickLauncherObject(headerTitle);
                 }
 
+                // If we are in a tablet in landscape mode then we will have a two pane view and we
+                // use the right pane to display the widgets table.
+                UiObject2 rightPane = mLauncher.findObjectInContainer(
+                        widgetPicker,
+                        widgetsContainerSelector);
+
                 // Look for a widgets list.
                 UiObject2 widgetsContainer = mLauncher.findObjectInContainer(
                         rightPane != null ? rightPane : widgetListView,
@@ -219,6 +219,13 @@
                 }
             }
             log("Finding test widget package - scroll with distance: " + scrollDistance);
+
+            // If we are in a tablet in landscape mode then we will have a two pane view and we use
+            // the right pane to display the widgets table.
+            UiObject2 rightPane = mLauncher.findObjectInContainer(
+                    widgetPicker,
+                    widgetsContainerSelector);
+
             mLauncher.scrollDownByDistance(hasHeaderExpanded && rightPane != null
                     ? rightPane
                     : widgetListView, scrollDistance);