Merge "Make LauncherState transitionDuration overridable at runtime" into ub-launcher3-master
diff --git a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
index 97eef66..22f1f23 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
@@ -30,6 +30,8 @@
 
     private static final String TAG = "GraphicsUtils";
 
+    public static Runnable sOnNewBitmapRunnable = () -> { };
+
     /**
      * Set the alpha component of {@code color} to be {@code alpha}. Unlike the support lib version,
      * it bounds the alpha in valid range instead of throwing an exception to allow for safer
@@ -77,5 +79,7 @@
     /**
      * Utility method to track new bitmap creation
      */
-    public static void noteNewBitmapCreated() { }
+    public static void noteNewBitmapCreated() {
+        sOnNewBitmapRunnable.run();
+    }
 }
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
index a7cd167..d94c665 100644
--- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
@@ -34,10 +34,10 @@
         android:orientation="vertical">
 
         <TextView
+            style="@style/TextHeadline"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="18dp"
-            android:fontFamily="google-sans"
             android:paddingLeft="@dimen/bottom_sheet_edu_padding"
             android:paddingRight="@dimen/bottom_sheet_edu_padding"
             android:text="@string/hotseat_migrate_title"
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index 06b9f1f..632b9b5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -317,8 +317,8 @@
                 && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
             return;
         }
-        if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
-            HotseatPredictionController.fillInHybridHotseatRank(itemInfo, target);
+        if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) {
+            HotseatPredictionController.encodeHotseatLayoutIntoPredictionRank(itemInfo, target);
             return;
         }
 
@@ -327,7 +327,7 @@
         IntStream.range(0, predictedApps.size())
                 .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
                 .findFirst()
-                .ifPresent((rank) -> target.predictedRank = rank);
+                .ifPresent((rank) -> target.predictedRank = 0 - rank);
     }
 
     public static class PredictionState {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index c2b55ab..cc6ec69 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
+import com.android.launcher3.FolderInfo;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
@@ -68,6 +69,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.OptionalInt;
 import java.util.stream.IntStream;
 
 /**
@@ -442,6 +444,20 @@
         mHotseat.invalidate();
     }
 
+    /**
+     * Unpins pinned app when it's converted into a folder
+     */
+    public void folderCreatedFromIcon(ItemInfo info, FolderInfo folderInfo) {
+        AppTarget target = getAppTargetFromItemInfo(info);
+        if (folderInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && !isInHotseat(
+                info)) {
+            notifyItemAction(target, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
+        } else if (folderInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
+                && folderInfo.screenId == Workspace.FIRST_SCREEN_ID && !isInFirstPage(info)) {
+            notifyItemAction(target, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
+        }
+    }
+
     @Override
     public void onDragEnd() {
         if (mDragObject == null) {
@@ -548,12 +564,11 @@
      * Fill in predicted_rank field based on app prediction.
      * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT
      */
-    public static void fillInHybridHotseatRank(
+    public static void encodeHotseatLayoutIntoPredictionRank(
             @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
         QuickstepLauncher launcher = QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
         if (launcher == null || launcher.getHotseatPredictionController() == null
-                || itemInfo.getTargetComponent() == null
-                || itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+                || itemInfo.getTargetComponent() == null) {
             return;
         }
         HotseatPredictionController controller = launcher.getHotseatPredictionController();
@@ -561,11 +576,12 @@
         final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
 
         final List<ComponentKeyMapper> predictedApps = controller.mComponentKeyMappers;
-        IntStream.range(0, predictedApps.size())
+        OptionalInt rank = IntStream.range(0, predictedApps.size())
                 .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
-                .findFirst()
-                .ifPresent((rank) -> target.predictedRank =
-                        Integer.parseInt(controller.mPredictedSpotsCount + "0" + rank));
+                .findFirst();
+
+        target.predictedRank = 10000 + (controller.mPredictedSpotsCount * 100)
+                + (rank.isPresent() ? rank.getAsInt() + 1 : 0);
     }
 
     private static boolean isPredictedIcon(View view) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index bd89626..b2e1798 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -47,6 +47,7 @@
 
     private static final float RING_EFFECT_RATIO = 0.11f;
 
+    boolean mIsDrawingDot = false;
     private final DeviceProfile mDeviceProfile;
     private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private boolean mIsPinned = false;
@@ -81,6 +82,16 @@
     }
 
     @Override
+    protected void drawDotIfNecessary(Canvas canvas) {
+        mIsDrawingDot = true;
+        int count = canvas.save();
+        canvas.translate(-getWidth() * RING_EFFECT_RATIO, -getHeight() * RING_EFFECT_RATIO);
+        super.drawDotIfNecessary(canvas);
+        canvas.restoreToCount(count);
+        mIsDrawingDot = false;
+    }
+
+    @Override
     public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
         super.applyFromWorkspaceItem(info);
         int color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
@@ -112,7 +123,7 @@
     @Override
     public void getIconBounds(Rect outBounds) {
         super.getIconBounds(outBounds);
-        if (!mIsPinned) {
+        if (!mIsPinned && !mIsDrawingDot) {
             int predictionInset = (int) (getIconSize() * RING_EFFECT_RATIO);
             outBounds.inset(predictionInset, predictionInset);
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index c359423..b87fcf2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -26,11 +26,14 @@
 import android.view.Gravity;
 
 import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.popup.SystemShortcut;
@@ -169,6 +172,16 @@
     }
 
     @Override
+    public FolderIcon addFolder(CellLayout layout, WorkspaceItemInfo info, int container,
+            int screenId, int cellX, int cellY) {
+        FolderIcon fi =  super.addFolder(layout, info, container, screenId, cellX, cellY);
+        if (mHotseatPredictionController != null) {
+            mHotseatPredictionController.folderCreatedFromIcon(info, fi.getFolder().getInfo());
+        }
+        return fi;
+    }
+
+    @Override
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
         if (mHotseatPredictionController != null) {
             return Stream.concat(super.getSupportedShortcuts(),
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index ff1b5f6..b80830a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -43,6 +43,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppTransitionManagerImpl;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -87,6 +88,10 @@
         if (handlingOverviewAnim()) {
             mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
         }
+
+        if (mAtomicAnim != null) {
+            mAtomicAnim.cancel();
+        }
     }
 
     protected void onMotionPauseChanged(boolean isPaused) {
@@ -193,13 +198,27 @@
 
         Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation(
                 INDEX_PAUSE_TO_OVERVIEW_ANIM);
-        overviewAnim.addListener(new AnimatorListenerAdapter() {
+        mAtomicAnim = new AnimatorSet();
+        mAtomicAnim.addListener(new AnimationSuccessListener() {
             @Override
-            public void onAnimationEnd(Animator animation) {
+            public void onAnimationSuccess(Animator animator) {
                 onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
             }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                if (mCancelled) {
+                    mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(mFromState,
+                            mToState, new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT,
+                            PEEK_OUT_ANIM_DURATION);
+                    mPeekAnim.start();
+                }
+                mAtomicAnim = null;
+            }
         });
-        overviewAnim.start();
+        mAtomicAnim.play(overviewAnim);
+        mAtomicAnim.start();
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 8b5283e..1b60404 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -21,7 +21,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
 import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
@@ -973,7 +973,7 @@
         }
         mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
 
-        if (UNSTABLE_SPRINGS.get()) {
+        if (QUICKSTEP_SPRINGS.get()) {
             mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
         }
         mLauncherTransitionController.getAnimationPlayer().start();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index c4733bd..fc50660 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -74,6 +74,10 @@
 
     @BinderThread
     public void onOverviewShown(boolean triggeredFromAltTab) {
+        if (triggeredFromAltTab) {
+            ActivityManagerWrapper.getInstance()
+                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+        }
         MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab));
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 2f73fc1..8ae4f06 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -2,6 +2,7 @@
 
 import android.view.MotionEvent;
 
+import com.android.launcher3.testing.TestLogging;
 import com.android.quickstep.InputConsumer;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -34,6 +35,7 @@
 
     protected void setActive(MotionEvent ev) {
         mState = STATE_ACTIVE;
+        TestLogging.recordEvent("pilferPointers");
         mInputMonitor.pilferPointers();
 
         // Send cancel event
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 5a34520..d01e1a4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -37,6 +37,7 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.util.DefaultDisplay;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
@@ -202,6 +203,7 @@
 
     private void startRecentsTransition() {
         mThresholdCrossed = true;
+        TestLogging.recordEvent("pilferPointers");
         mInputMonitorCompat.pilferPointers();
 
         Intent intent = new Intent(Intent.ACTION_MAIN)
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index bf2128d..1b0e05a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -44,6 +44,7 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.R;
+import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.BaseActivityInterface;
@@ -302,6 +303,7 @@
         if (mInteractionHandler == null) {
             return;
         }
+        TestLogging.recordEvent("pilferPointers");
         mInputMonitorCompat.pilferPointers();
 
         mActivityInterface.closeOverlay();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index c19754f..32a67ca 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -25,6 +25,7 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
@@ -106,6 +107,7 @@
                 ActiveGestureLog.INSTANCE.addLog("startQuickstep");
             }
             if (mInputMonitor != null) {
+                TestLogging.recordEvent("pilferPointers");
                 mInputMonitor.pilferPointers();
             }
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index ca15ca1..6bfc3fd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -22,6 +22,7 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.logging.StatsLogUtils;
+import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.quickstep.GestureState;
@@ -63,6 +64,7 @@
 
     private void onInterceptTouch() {
         if (mInputMonitor != null) {
+            TestLogging.recordEvent("pilferPointers");
             mInputMonitor.pilferPointers();
         }
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 4a39e73..9e29238 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -224,7 +224,6 @@
             surfaceParams[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer,
                     cornerRadius / scale);
         }
-        applySurfaceParams(params.syncTransactionApplier, surfaceParams);
         return surfaceParams;
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 3bc1509..49f667e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -20,7 +20,6 @@
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
@@ -38,11 +37,11 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Property;
+import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@@ -102,7 +101,7 @@
     private float mSaturation = 1f;
 
     private boolean mOverlayEnabled;
-    private boolean mRotated;
+    private boolean mIsOrientationChanged;
     private OverviewScreenshotActions mOverviewScreenshotActionsPlugin;
 
     public TaskThumbnailView(Context context) {
@@ -323,7 +322,7 @@
 
     private void updateOverlay() {
         // The overlay doesn't really work when the screenshot is rotated, so don't add it.
-        if (mOverlayEnabled && !mRotated && mBitmapShader != null && mThumbnailData != null) {
+        if (mOverlayEnabled && !mIsOrientationChanged && mBitmapShader != null && mThumbnailData != null) {
             mOverlay.initOverlay(mTask, mThumbnailData, mMatrix);
         } else {
             mOverlay.reset();
@@ -346,6 +345,7 @@
 
     private void updateThumbnailMatrix() {
         boolean isRotated = false;
+        boolean isOrientationDifferent = false;
         mClipBottom = -1;
         if (mBitmapShader != null && mThumbnailData != null) {
             float scale = mThumbnailData.scale;
@@ -356,51 +356,40 @@
                     (thumbnailInsets.top + thumbnailInsets.bottom) * scale;
 
             final float thumbnailScale;
-            final DeviceProfile profile = mActivity.getDeviceProfile();
-
+            int thumbnailRotation = mThumbnailData.rotation;
+            int currentRotation = getDisplay() != null ? getDisplay().getRotation() : 0;
+            int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
+            // Landscape vs portrait change
+            boolean windowingModeSupportsRotation = !mActivity.isInMultiWindowMode()
+                    && mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
+            isOrientationDifferent = isOrientationChange(deltaRotate)
+                && windowingModeSupportsRotation;
             if (getMeasuredWidth() == 0) {
                 // If we haven't measured , skip the thumbnail drawing and only draw the background
                 // color
                 thumbnailScale = 0f;
             } else {
-                final Configuration configuration =
-                        getContext().getResources().getConfiguration();
                 // Rotate the screenshot if not in multi-window mode
-                isRotated = configuration.orientation != mThumbnailData.orientation &&
-                        !mActivity.isInMultiWindowMode() &&
-                        mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
+                isRotated = deltaRotate > 0 && windowingModeSupportsRotation;
                 // Scale the screenshot to always fit the width of the card.
-                thumbnailScale = isRotated
+
+                thumbnailScale = isOrientationDifferent
                         ? getMeasuredWidth() / thumbnailHeight
                         : getMeasuredWidth() / thumbnailWidth;
             }
 
-            if (isRotated) {
-                int rotationDir = profile.isVerticalBarLayout() && !profile.isSeascape() ? -1 : 1;
-                mMatrix.setRotate(90 * rotationDir);
-                int newLeftInset = rotationDir == 1 ? thumbnailInsets.bottom : thumbnailInsets.top;
-                int newTopInset = rotationDir == 1 ? thumbnailInsets.left : thumbnailInsets.right;
-                mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale);
-                if (rotationDir == -1) {
-                    // Crop the right/bottom side of the screenshot rather than left/top
-                    float excessHeight = thumbnailWidth * thumbnailScale - getMeasuredHeight();
-                    mClippedInsets.offset(0, excessHeight);
-                }
-                mMatrix.postTranslate(-mClippedInsets.left, -mClippedInsets.top);
-                // Move the screenshot to the thumbnail window (rotation moved it out).
-                if (rotationDir == 1) {
-                    mMatrix.postTranslate(mThumbnailData.thumbnail.getHeight(), 0);
-                } else {
-                    mMatrix.postTranslate(0, mThumbnailData.thumbnail.getWidth());
-                }
-            } else {
-                mClippedInsets.offsetTo(thumbnailInsets.left * scale, thumbnailInsets.top * scale);
+            if (!isRotated) {
+                // No Rotation
+                mClippedInsets.offsetTo(thumbnailInsets.left * scale,
+                    thumbnailInsets.top * scale);
                 mMatrix.setTranslate(-mClippedInsets.left, -mClippedInsets.top);
+            } else {
+                setThumbnailRotation(deltaRotate, thumbnailInsets, scale);
             }
 
             final float widthWithInsets;
             final float heightWithInsets;
-            if (isRotated) {
+            if (isOrientationDifferent) {
                 widthWithInsets = mThumbnailData.thumbnail.getHeight() * thumbnailScale;
                 heightWithInsets = mThumbnailData.thumbnail.getWidth() * thumbnailScale;
             } else {
@@ -415,7 +404,7 @@
             mMatrix.postScale(thumbnailScale, thumbnailScale);
             mBitmapShader.setLocalMatrix(mMatrix);
 
-            float bitmapHeight = Math.max((isRotated ? thumbnailWidth : thumbnailHeight)
+            float bitmapHeight = Math.max((isOrientationDifferent ? thumbnailWidth : thumbnailHeight)
                     * thumbnailScale, 0);
             if (Math.round(bitmapHeight) < getMeasuredHeight()) {
                 mClipBottom = bitmapHeight;
@@ -423,7 +412,7 @@
             mPaint.setShader(mBitmapShader);
         }
 
-        mRotated = isRotated;
+        mIsOrientationChanged = isOrientationDifferent;
         invalidate();
 
         // Update can be called from {@link #onSizeChanged} during layout, post handling of overlay
@@ -431,6 +420,51 @@
         post(this::updateOverlay);
     }
 
+    private int getRotationDelta(int oldRotation, int newRotation) {
+        int delta = newRotation - oldRotation;
+        if (delta < 0) delta += 4;
+        return delta;
+    }
+
+    /**
+     * @param deltaRotation the number of 90 degree turns from the current orientation
+     * @return {@code true} if the change in rotation results in a shift from landscape to portrait
+     * or vice versa, {@code false} otherwise
+     */
+    private boolean isOrientationChange(int deltaRotation) {
+        return deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270;
+    }
+
+    private void setThumbnailRotation(int deltaRotate, Rect thumbnailInsets, float scale) {
+        int newLeftInset = 0;
+        int newTopInset = 0;
+        int translateX = 0;
+        int translateY = 0;
+
+        mMatrix.setRotate(90 * deltaRotate);
+        switch (deltaRotate) { /* Counter-clockwise */
+            case Surface.ROTATION_90:
+                newLeftInset = thumbnailInsets.bottom;
+                newTopInset = thumbnailInsets.left;
+                translateX = mThumbnailData.thumbnail.getHeight();
+                break;
+            case Surface.ROTATION_270:
+                newLeftInset = thumbnailInsets.top;
+                newTopInset = thumbnailInsets.right;
+                translateY = mThumbnailData.thumbnail.getWidth();
+                break;
+            case Surface.ROTATION_180:
+                newLeftInset = -thumbnailInsets.top;
+                newTopInset = -thumbnailInsets.left;
+                translateX = mThumbnailData.thumbnail.getWidth();
+                translateY = mThumbnailData.thumbnail.getHeight();
+                break;
+        }
+        mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale);
+        mMatrix.postTranslate(translateX - mClippedInsets.left,
+                translateY - mClippedInsets.top);
+    }
+
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 58e36bc..5954b86 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -243,6 +243,7 @@
                         getResources().getDimensionPixelSize(R.dimen.overview_actions_height),
                         Gravity.BOTTOM);
                 addView(mActionsView, params);
+                mActionsView.setAlpha(0);
             }
         }
     }
@@ -447,7 +448,8 @@
         mIconView.setScaleX(scale);
         mIconView.setScaleY(scale);
 
-        if (mActionsView != null) {
+
+        if (mActionsView != null && isRunningTask()) {
             mActionsView.setAlpha(scale);
         }
 
@@ -523,9 +525,11 @@
     public void onPageScroll(ScrollState scrollState) {
         float curveInterpolation =
                 CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
+        float curveScaleForCurveInterpolation = getCurveScaleForCurveInterpolation(
+                curveInterpolation);
 
         mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
-        setCurveScale(getCurveScaleForCurveInterpolation(curveInterpolation));
+        setCurveScale(curveScaleForCurveInterpolation);
 
         mFooterAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f);
         for (FooterWrapper footer : mFooters) {
@@ -539,6 +543,28 @@
             mMenuView.setScaleX(getScaleX());
             mMenuView.setScaleY(getScaleY());
         }
+
+        // This is not the proper implementation and will be replaced with a proper layout.
+        if (mActionsView != null) {
+            if (mFocusTransitionProgress == 1f) {
+                mActionsView.setAlpha(1 - curveInterpolation / MAX_PAGE_SCRIM_ALPHA);
+            }
+            maintainActionViewPosition(curveScaleForCurveInterpolation);
+        }
+
+    }
+
+    private void maintainActionViewPosition(float curveScaleForCurveInterpolation) {
+        float inverseCurveScaleFactor = curveScaleForCurveInterpolation == 0 ? 0 :
+                (1f / curveScaleForCurveInterpolation);
+        mActionsView.setScaleX(inverseCurveScaleFactor);
+        mActionsView.setScaleY(inverseCurveScaleFactor);
+        mActionsView.setTranslationX(inverseCurveScaleFactor * (-getX()
+                + getRecentsView().getScrollX() + getRecentsView().scrollOffsetLeft()));
+        mActionsView.setTranslationY(
+                (1f - curveScaleForCurveInterpolation) * (mSnapshotView.getHeight()
+                        + mActionsView.getHeight()) / 2f
+                        + inverseCurveScaleFactor * (-getTranslationY()));
     }
 
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index d5ce734..99b2a81 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -25,7 +25,7 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
 import android.animation.TimeInterpolator;
@@ -277,7 +277,7 @@
     private void handleFirstSwipeToOverview(final ValueAnimator animator,
             final long expectedDuration, final LauncherState targetState, final float velocity,
             final boolean isFling) {
-        if (UNSTABLE_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
+        if (QUICKSTEP_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
                 && targetState == OVERVIEW) {
             mFinishFastOnSecondTouch = true;
         } else  if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index bd2c539..a726052 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -147,7 +147,7 @@
     @Test
     @Ignore // b/143488140
     public void goToOverviewFromApp() {
-        startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
 
         mLauncher.getBackground().switchToOverview();
     }
@@ -182,8 +182,8 @@
     @Test
     @Ignore // b/143488140
     public void testOverview() {
-        startAppFastAndWaitForRecentTask(getAppPackageName());
-        startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+        startAppFast(getAppPackageName());
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
         startTestActivity(2);
         Wait.atMost("Expected three apps in the task list",
                 () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
@@ -236,25 +236,4 @@
     private int getTaskCount(RecentsActivity recents) {
         return recents.<RecentsView>getOverviewPanel().getTaskViewCount();
     }
-
-    /**
-     * Workaround for b/141580748, there was an issue where the recent task is only updated when the
-     * activity starting the task is resumed.  In this case, we should wait until the task is in
-     * the recents task list before continuing.
-     */
-    private void startAppFastAndWaitForRecentTask(String packageName) {
-        startAppFast(packageName);
-        Wait.atMost("Expected app in task list",
-                () -> containsRecentTaskWithPackage(packageName), DEFAULT_ACTIVITY_TIMEOUT,
-                mLauncher);
-    }
-
-    private boolean containsRecentTaskWithPackage(String packageName) {
-        for (ComponentName cn : mLauncher.getRecentTasks()) {
-            if (cn.getPackageName().equals(packageName)) {
-                return true;
-            }
-        }
-        return false;
-    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index c99df10..d2f5d8f 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -17,6 +17,8 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -40,6 +42,7 @@
 import com.android.launcher3.tapl.OverviewTask;
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.rule.TestStabilityRule;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 import com.android.quickstep.views.RecentsView;
 
@@ -268,7 +271,10 @@
     @Test
     @NavigationModeSwitch
     @PortraitLandscape
-    @Ignore // b/143285809
+    // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+    @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
+    // b/143285809
+    @Ignore
     public void testQuickSwitchFromApp() throws Exception {
         startTestActivity(2);
         startTestActivity(3);
diff --git a/res/drawable/ic_block.xml b/res/drawable/ic_block.xml
new file mode 100644
index 0000000..edeb4c6
--- /dev/null
+++ b/res/drawable/ic_block.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:tint="?android:attr/textColorPrimary">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M4,12c0-4.42,3.58-8,8-8c1.85,0,3.55,0.63,4.9,1.69
+	L5.69,16.9C4.63,15.55,4,13.85,4,12z M12,20c-1.85,0-3.55-0.63-4.9-1.69L18.31,7.1C19.37,8.45,20,10.15,20,12
+	C20,16.42,16.42,20,12,20z" />
+</vector>
diff --git a/res/drawable/ic_corp.xml b/res/drawable/ic_corp.xml
index b59113d..76dccd3 100644
--- a/res/drawable/ic_corp.xml
+++ b/res/drawable/ic_corp.xml
@@ -19,9 +19,5 @@
     android:viewportWidth="24.0"
     android:viewportHeight="24.0"
     android:tint="?android:attr/textColorHint" >
-    <path
-        android:pathData="M20 6h-4V4c0-1.11-0.89-2-2-2h-4c-1.11 0-2 0.89-2 2v2H4c-1.11 0-1.99 0.89 -1.99
-2L2 19c0 1.11 0.89 2 2 2h16c1.11 0 2-0.89 2-2V8c0-1.11-0.89-2-2-2zM10
-4h4v2h-4V4zm10 15H4V8h16v11z"
-        android:fillColor="@android:color/white"/>
+    <path android:fillColor="@android:color/white" android:pathData="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-8 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm2-9h-4V4h4v2z"/>
 </vector>
\ No newline at end of file
diff --git a/res/drawable/ic_corp_off.xml b/res/drawable/ic_corp_off.xml
new file mode 100644
index 0000000..62a9787
--- /dev/null
+++ b/res/drawable/ic_corp_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.0"
+    android:viewportHeight="24.0"
+    android:tint="?android:attr/textColorHint" >
+    <path
+        android:pathData="M22 7.95c.05-1.11-.84-2-1.95-1.95H16V3.95c0-1.11-.84-2-1.95-1.95h-4C8.94 1.95 8 2.84 8 3.95v.32l14 14V7.95zM14 6h-4V4h4v2zm7.54 14.28l-7.56-7.56v.01l-1.7-1.7h.01L7.21 5.95 3.25 1.99 1.99 3.27 4.69 6h-.64c-1.11 0-1.99.86-1.99 1.97l-.01 11.02c0 1.11.89 2.01 2 2.01h15.64l2.05 2.02L23 21.75l-1.46-1.47z"
+        android:fillColor="@android:color/white"/>
+</vector>
\ No newline at end of file
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 0da51d9..a41fb9a 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -57,7 +57,6 @@
                 android:layout_weight="1"
                 android:background="?android:attr/selectableItemBackground"
                 android:text="@string/all_apps_personal_tab"
-                android:textAllCaps="true"
                 android:textColor="@color/all_apps_tab_text"
                 android:textSize="14sp" />
 
@@ -68,7 +67,6 @@
                 android:layout_weight="1"
                 android:background="?android:attr/selectableItemBackground"
                 android:text="@string/all_apps_work_tab"
-                android:textAllCaps="true"
                 android:textColor="@color/all_apps_tab_text"
                 android:textSize="14sp" />
         </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
new file mode 100644
index 0000000..5607e78
--- /dev/null
+++ b/res/layout/work_apps_paused.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="8dp"
+    android:orientation="vertical"
+    android:background="?attr/allAppsScrimColor"
+    android:gravity="center">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:contentDescription="@string/work_apps_paused_title"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:tint="?attr/folderTextColor"
+        android:src="@drawable/ic_corp_off" />
+
+    <TextView
+        style="@style/TextHeadline"
+        android:textColor="?attr/folderTextColor"
+        android:id="@+id/work_apps_paused_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:layout_marginBottom="8dp"
+        android:text="@string/work_apps_paused_title"
+        android:textAlignment="center"
+        android:textSize="24sp" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="?attr/folderTextColor"
+        android:text="@string/work_apps_paused_body"
+        android:textAlignment="center"
+        android:textSize="16sp" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/work_profile_edu.xml b/res/layout/work_profile_edu.xml
index a8e3d20..f7a529d 100644
--- a/res/layout/work_profile_edu.xml
+++ b/res/layout/work_profile_edu.xml
@@ -35,12 +35,12 @@
         android:paddingRight="@dimen/bottom_sheet_edu_padding">
 
         <TextView
+            style="@style/TextHeadline"
             android:id="@+id/content_text"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="48dp"
             android:layout_marginBottom="48dp"
-            android:fontFamily="google-sans"
             android:text="@string/work_profile_edu_personal_apps"
             android:textAlignment="center"
             android:textColor="@android:color/white"
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
index 379e9d0..db95416 100644
--- a/res/layout/work_tab_footer.xml
+++ b/res/layout/work_tab_footer.xml
@@ -17,63 +17,31 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:id="@+id/work_toggle_container"
     android:focusable="true"
-    android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_bottom_padding"
-    android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
-    android:paddingRight="@dimen/dynamic_grid_cell_padding_x"
-    android:paddingTop="@dimen/all_apps_work_profile_tab_footer_top_padding">
-
-    <ImageView
-        android:id="@+id/work_footer_divider"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:focusable="false"
-        android:importantForAccessibility="no"
-        android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
-        android:paddingTop="@dimen/all_apps_divider_margin_vertical"
-        android:scaleType="fitXY"
-        android:src="@drawable/all_apps_divider"/>
-
-    <com.android.launcher3.allapps.WorkModeSwitch
-        android:id="@+id/work_mode_toggle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentEnd="true"
-        android:layout_below="@id/work_footer_divider"/>
+    android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_padding_vertical"
+    android:orientation="horizontal"
+    android:paddingLeft="@dimen/all_apps_work_profile_tab_footer_padding_horizontal"
+    android:background="?attr/allAppsScrimColor"
+    android:paddingRight="@dimen/all_apps_work_profile_tab_footer_padding_horizontal"
+    android:paddingTop="@dimen/all_apps_work_profile_tab_footer_padding_vertical">
 
     <TextView
-        android:id="@android:id/title"
-        android:layout_width="wrap_content"
+        android:id="@+id/work_mode_label"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:drawableStart="@drawable/ic_corp"
+        android:drawablePadding="3dp"
         android:layout_height="wrap_content"
-        android:layout_alignBaseline="@id/work_mode_toggle"
-        android:layout_alignParentStart="true"
-        android:ellipsize="end"
-        android:lines="1"
-        android:text="@string/work_profile_toggle_label"
-        android:textColor="?android:attr/textColorTertiary"
-        android:textSize="16sp"/>
-
-    <ImageView
-        android:id="@android:id/icon"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_below="@android:id/title"
-        android:layout_marginTop="8dp"
-        android:src="@drawable/ic_corp"/>
-
-    <TextView
-        android:id="@+id/managed_by_label"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@android:id/title"
-        android:layout_marginTop="8dp"
-        android:layout_toEndOf="@android:id/icon"
         android:ellipsize="end"
         android:gravity="center_vertical"
         android:lines="1"
         android:minHeight="24dp"
-        android:paddingStart="12dp"
-        android:textColor="?android:attr/textColorHint"
-        android:textSize="13sp"/>
+        android:paddingEnd="12dp"
+        android:textSize="16sp"/>
+    <com.android.launcher3.allapps.WorkModeSwitch
+        android:id="@+id/work_mode_toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
 
 </com.android.launcher3.views.WorkFooterContainer>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index 0945642..ef34dcd 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -108,6 +108,7 @@
     <item type="id" name="action_shortcuts_and_notifications"/>
     <item type="id" name="action_dismiss_notification" />
     <item type="id" name="action_remote_action_shortcut" />
+    <item type="id" name="action_dismiss_prediction" />
 
     <!-- QSB IDs. DO not change -->
     <item type="id" name="search_container_workspace" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0293573..edae7f4 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -85,6 +85,9 @@
     <dimen name="all_apps_tabs_side_padding">12dp</dimen>
     <dimen name="all_apps_divider_height">1dp</dimen>
 
+    <dimen name="all_apps_work_profile_tab_footer_padding_vertical">20dp</dimen>
+    <dimen name="all_apps_work_profile_tab_footer_padding_horizontal">24dp</dimen>
+
 <!-- Search bar in All Apps -->
     <dimen name="all_apps_header_max_elevation">3dp</dimen>
     <dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3f279f4..bfa92f7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -338,9 +338,12 @@
 
     <!-- This string is in the work profile tab when a user has All Apps open on their phone. It describes the label of a toggle, "Work profile," as being managed by the user's employer.
     "Organization" is used to represent a variety of businesses, non-profits, and educational institutions).-->
-    <string name="work_mode_on_label">Managed by your organization</string>
+    <string name="work_mode_on_label">Work apps: On</string>
     <!-- This string appears under a the label of a toggle in the work profile tab on a user's phone. It describes the status of the toggle, "Work profile," when it's turned off. "Work profile" means a separate profile on a user's phone that's speficially for their work apps and is managed by their company.-->
-    <string name="work_mode_off_label">Notifications and apps are off</string>
+    <string name="work_mode_off_label">Work apps: Paused</string>
+
+    <string name="work_apps_paused_title">Work apps are paused</string>
+    <string name="work_apps_paused_body">You won\'t get any work notifications, and your IT admin can\'t see your location</string>
 
     <!-- Failed action error message: e.g. Failed: Pause -->
     <string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 80c791c..35ae49c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -55,6 +55,8 @@
         <item name="android:windowDrawsSystemBarBackgrounds">true</item>
         <item name="android:statusBarColor">#00000000</item>
         <item name="android:navigationBarColor">#00000000</item>
+
+
     </style>
 
     <style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme">
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 3f723d1..c99465c 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -214,4 +214,11 @@
         return id;
     }
 
+    /**
+     * Returns if an Item is a predicted item
+     */
+    public boolean isPredictedItem() {
+        return container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
+                || container == LauncherSettings.Favorites.CONTAINER_PREDICTION;
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 445ebc0..43540ce 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -32,7 +32,6 @@
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.LoggerUtils.newTarget;
 import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
-import static com.android.launcher3.popup.SystemShortcut.DISMISS_PREDICTION;
 import static com.android.launcher3.popup.SystemShortcut.INSTALL;
 import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
@@ -1721,8 +1720,11 @@
         }
     }
 
-    FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX,
-            int cellY) {
+    /**
+     * Creates and adds new folder to CellLayout
+     */
+    public FolderIcon addFolder(CellLayout layout, WorkspaceItemInfo info, int container,
+            final int screenId, int cellX, int cellY) {
         final FolderInfo folderInfo = new FolderInfo();
         folderInfo.title = "";
 
@@ -1730,7 +1732,8 @@
         getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
 
         // Create the view
-        FolderIcon newFolder = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this, layout, folderInfo);
+        FolderIcon newFolder = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this, layout,
+                folderInfo);
         mWorkspace.addInScreen(newFolder, folderInfo);
         // Force measure the new folder icon
         CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder);
@@ -2691,7 +2694,7 @@
     }
 
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
-        return Stream.of(APP_INFO, WIDGETS, INSTALL, DISMISS_PREDICTION);
+        return Stream.of(APP_INFO, WIDGETS, INSTALL);
     }
 
     public static Launcher getLauncher(Context context) {
diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java
index 95ee687..5f6ecb5 100644
--- a/src/com/android/launcher3/MainProcessInitializer.java
+++ b/src/com/android/launcher3/MainProcessInitializer.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapCreationCheck;
 import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.util.ResourceBasedOverride;
@@ -39,5 +40,9 @@
         FeatureFlags.initialize(context);
         SessionCommitReceiver.applyDefaultUserPrefs(context);
         IconShape.init(context);
+
+        if (BitmapCreationCheck.ENABLED) {
+            BitmapCreationCheck.startTracking(context);
+        }
     }
 }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index a1888bf..ae4eae9 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -245,6 +245,13 @@
         forceFinishScroller(true);
     }
 
+    /**
+     * Returns left offset of a page. This is the gap between pages and prevents overlap.
+     */
+    public int scrollOffsetLeft() {
+        return mInsets.left + getPaddingLeft();
+    }
+
     private void abortScrollerAnimation(boolean resetNextPage) {
         mScroller.abortAnimation();
         // We need to clean up the next page here to avoid computeScrollHelper from
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 3c2ed72..1dbe195 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -6,6 +6,7 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
 
@@ -29,9 +30,11 @@
 import android.widget.Toast;
 
 import com.android.launcher3.Launcher.OnResumeCallback;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -43,6 +46,7 @@
  * Drop target which provides a secondary option for an item.
  *    For app targets: shows as uninstall
  *    For configurable widgets: shows as setup
+ *    For predicted app icons: don't suggest app
  */
 public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmListener {
 
@@ -81,7 +85,11 @@
             mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
             setDrawable(R.drawable.ic_uninstall_shadow);
             updateText(R.string.uninstall_drop_target_label);
-        } else {
+        } else if (action == DISMISS_PREDICTION) {
+            mHoverColor = Themes.getColorAccent(getContext());
+            setDrawable(R.drawable.ic_block);
+            updateText(R.string.dismiss_prediction_label);
+        } else if (action == RECONFIGURE) {
             mHoverColor = Themes.getColorAccent(getContext());
             setDrawable(R.drawable.ic_setup_shadow);
             updateText(R.string.gadget_setup_text);
@@ -101,8 +109,13 @@
     @Override
     public Target getDropTargetForLogging() {
         Target t = LoggerUtils.newTarget(Target.Type.CONTROL);
-        t.controlType = mCurrentAccessibilityAction == UNINSTALL ? ControlType.UNINSTALL_TARGET
-                : ControlType.SETTINGS_BUTTON;
+        if (mCurrentAccessibilityAction == UNINSTALL) {
+            t.controlType = ControlType.UNINSTALL_TARGET;
+        } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
+            t.controlType = ControlType.DISMISS_PREDICTION;
+        } else {
+            t.controlType = ControlType.SETTINGS_BUTTON;
+        }
         return t;
     }
 
@@ -119,6 +132,9 @@
                 return true;
             }
             return false;
+        } else if (FeatureFlags.ENABLE_PREDICTION_DISMISS.get() && info.isPredictedItem()) {
+            setupUi(DISMISS_PREDICTION);
+            return true;
         }
 
         setupUi(UNINSTALL);
@@ -229,6 +245,11 @@
             }
             return null;
         }
+        if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
+            AppLaunchTracker.INSTANCE.get(getContext()).onDismissApp(info.getTargetComponent(),
+                    info.user, AppLaunchTracker.CONTAINER_PREDICTIONS);
+            return null;
+        }
         // else: mCurrentAccessibilityAction == UNINSTALL
 
         ComponentName cn = getUninstallTarget(info);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 377b825..b725002 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1702,8 +1702,8 @@
             float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
             target.removeView(v);
 
-            FolderIcon fi =
-                mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
+            FolderIcon fi = mLauncher.addFolder(target, sourceInfo, container, screenId,
+                    targetCell[0], targetCell[1]);
             destInfo.cellX = -1;
             destInfo.cellY = -1;
             sourceInfo.cellX = -1;
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index ed869bb..0b439ec 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -54,6 +54,7 @@
 
     public static final int REMOVE = R.id.action_remove;
     public static final int UNINSTALL = R.id.action_uninstall;
+    public static final int DISMISS_PREDICTION = R.id.action_dismiss_prediction;
     public static final int RECONFIGURE = R.id.action_reconfigure;
     protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
     protected static final int MOVE = R.id.action_move;
@@ -86,6 +87,8 @@
                 launcher.getText(R.string.remove_drop_target_label)));
         mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL,
                 launcher.getText(R.string.uninstall_drop_target_label)));
+        mActions.put(DISMISS_PREDICTION, new AccessibilityAction(DISMISS_PREDICTION,
+                launcher.getText(R.string.dismiss_prediction_label)));
         mActions.put(RECONFIGURE, new AccessibilityAction(RECONFIGURE,
                 launcher.getText(R.string.gadget_setup_text)));
         mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE,
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 6f15c9b..56bd1b6 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.allapps;
 
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -51,6 +54,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -59,6 +63,7 @@
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 import com.android.launcher3.views.SpringRelativeLayout;
+import com.android.launcher3.views.WorkFooterContainer;
 
 /**
  * The all apps view container.
@@ -83,7 +88,10 @@
     protected SearchUiManager mSearchUiManager;
     private View mSearchContainer;
     private AllAppsPagedView mViewPager;
+
     private FloatingHeaderView mHeader;
+    private WorkFooterContainer mWorkFooterContainer;
+
 
     private SpannableStringBuilder mSearchQueryBuilder = null;
 
@@ -173,6 +181,15 @@
             }
         }
         rebindAdapters(hasWorkApps);
+        if (hasWorkApps) {
+            resetWorkProfile();
+        }
+    }
+
+    private void resetWorkProfile() {
+        mWorkFooterContainer.refresh();
+        mAH[AdapterHolder.WORK].setupOverlay();
+        mAH[AdapterHolder.WORK].applyPadding();
     }
 
     /**
@@ -311,6 +328,7 @@
             mAH[i].padding.bottom = insets.bottom;
             mAH[i].padding.left = mAH[i].padding.right = leftRightPadding;
             mAH[i].applyPadding();
+            mAH[i].setupOverlay();
         }
 
         ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
@@ -368,12 +386,17 @@
         mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.WORK].recyclerView);
 
         if (mUsingTabs) {
+            setupWorkToggle();
             mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
             mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
             onTabChanged(mViewPager.getNextPage());
         } else {
             mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
             mAH[AdapterHolder.WORK].recyclerView = null;
+            if (mWorkFooterContainer != null) {
+                ((ViewGroup) mWorkFooterContainer.getParent()).removeView(mWorkFooterContainer);
+                mWorkFooterContainer = null;
+            }
         }
         setupHeader();
 
@@ -381,6 +404,16 @@
         mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
     }
 
+    private void setupWorkToggle() {
+        mWorkFooterContainer = (WorkFooterContainer) mLauncher.getLayoutInflater().inflate(
+                R.layout.work_tab_footer, findViewById(R.id.work_toggle_container));
+        mWorkFooterContainer.setLayoutParams(
+                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT));
+        this.addView(mWorkFooterContainer);
+        mWorkFooterContainer.post(() -> mAH[AdapterHolder.WORK].applyPadding());
+    }
+
     private void replaceRVContainer(boolean showTabs) {
         for (int i = 0; i < mAH.length; i++) {
             if (mAH[i].recyclerView != null) {
@@ -417,6 +450,9 @@
             findViewById(R.id.tab_work)
                     .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
         }
+        if (mWorkFooterContainer != null) {
+            mWorkFooterContainer.setWorkTabVisible(pos == AdapterHolder.WORK);
+        }
     }
 
     // Used by tests only
@@ -562,6 +598,7 @@
         public static final int MAIN = 0;
         public static final int WORK = 1;
 
+        private final boolean mIsWork;
         public final AllAppsGridAdapter adapter;
         final LinearLayoutManager layoutManager;
         final AlphabeticalAppsList appsList;
@@ -569,7 +606,10 @@
         AllAppsRecyclerView recyclerView;
         boolean verticalFadingEdge;
 
+        boolean mWorkDisabled;
+
         AdapterHolder(boolean isWork) {
+            mIsWork = isWork;
             appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
             adapter = new AllAppsGridAdapter(mLauncher, appsList);
             appsList.setAdapter(adapter);
@@ -591,11 +631,36 @@
             adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
             applyVerticalFadingEdgeEnabled(verticalFadingEdge);
             applyPadding();
+            setupOverlay();
+        }
+
+        void setupOverlay() {
+            if (!mIsWork || recyclerView == null) return;
+            boolean workDisabled = UserCache.INSTANCE.get(mLauncher).isAnyProfileQuietModeEnabled();
+            recyclerView.getOverlay().clear();
+            if (workDisabled) {
+                View pausedOverlay = mLauncher.getLayoutInflater().inflate(
+                        R.layout.work_apps_paused, null);
+                recyclerView.post(() -> {
+                    int width = recyclerView.getWidth();
+                    int height = recyclerView.getHeight();
+                    pausedOverlay.measure(makeMeasureSpec(width, EXACTLY),
+                            makeMeasureSpec(height, EXACTLY));
+                    pausedOverlay.layout(0, 0, width, height);
+                    applyPadding();
+                });
+                recyclerView.getOverlay().add(pausedOverlay);
+            }
+            mWorkDisabled = workDisabled;
         }
 
         void applyPadding() {
             if (recyclerView != null) {
-                recyclerView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
+                int bottomOffset =
+                        mWorkFooterContainer != null && mIsWork ? mWorkFooterContainer.getHeight()
+                                : 0;
+                recyclerView.setPadding(padding.left, padding.top, padding.right,
+                        padding.bottom + bottomOffset);
             }
         }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 442b77b..1f861bc 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -43,7 +43,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
 import com.android.launcher3.model.AppLaunchTracker;
-import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.List;
@@ -67,7 +66,6 @@
 
     // A divider that separates the apps list and the search market button
     public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
-    public static final int VIEW_TYPE_WORK_TAB_FOOTER = 1 << 5;
 
     // Common view type masks
     public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
@@ -289,9 +287,6 @@
             case VIEW_TYPE_ALL_APPS_DIVIDER:
                 return new ViewHolder(mLayoutInflater.inflate(
                         R.layout.all_apps_divider, parent, false));
-            case VIEW_TYPE_WORK_TAB_FOOTER:
-                View footer = mLayoutInflater.inflate(R.layout.work_tab_footer, parent, false);
-                return new ViewHolder(footer);
             default:
                 throw new RuntimeException("Unexpected view type");
         }
@@ -323,15 +318,6 @@
             case VIEW_TYPE_ALL_APPS_DIVIDER:
                 // nothing to do
                 break;
-            case VIEW_TYPE_WORK_TAB_FOOTER:
-                WorkModeSwitch workModeToggle = holder.itemView.findViewById(R.id.work_mode_toggle);
-                workModeToggle.refresh();
-                TextView managedByLabel = holder.itemView.findViewById(R.id.managed_by_label);
-                boolean anyProfileQuietModeEnabled = UserCache.INSTANCE.get(
-                        managedByLabel.getContext()).isAnyProfileQuietModeEnabled();
-                managedByLabel.setText(anyProfileQuietModeEnabled
-                        ? R.string.work_mode_off_label : R.string.work_mode_on_label);
-                break;
         }
         if (mBindViewCallback != null) {
             mBindViewCallback.onBindView(holder);
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 0681919..1bde138 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -11,7 +11,7 @@
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
 
 import android.animation.Animator;
@@ -46,7 +46,7 @@
  */
 public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener {
 
-    private static final float SPRING_DAMPING_RATIO = 0.9f;
+    private static final float SPRING_DAMPING_RATIO = 0.75f;
     private static final float SPRING_STIFFNESS = 600f;
 
     public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
@@ -185,7 +185,7 @@
     }
 
     public Animator createSpringAnimation(float... progressValues) {
-        if (UNSTABLE_SPRINGS.get()) {
+        if (QUICKSTEP_SPRINGS.get()) {
             return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
                     SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
         }
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index a33fe4d..b501c82 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -15,14 +15,11 @@
  */
 package com.android.launcher3.allapps;
 
-import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LabelComparator;
@@ -117,13 +114,6 @@
             item.position = pos;
             return item;
         }
-
-        public static AdapterItem asWorkTabFooter(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.VIEW_TYPE_WORK_TAB_FOOTER;
-            item.position = pos;
-            return item;
-        }
     }
 
     private final BaseDraggingActivity mLauncher;
@@ -390,18 +380,6 @@
                     break;
             }
         }
-
-        // Add the work profile footer if required.
-        if (shouldShowWorkFooter()) {
-            mAdapterItems.add(AdapterItem.asWorkTabFooter(position++));
-        }
-    }
-
-    private boolean shouldShowWorkFooter() {
-        return mIsWork && Utilities.ATLEAST_P &&
-                (hasShortcutsPermission(mLauncher)
-                        || mLauncher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
-                        == PackageManager.PERMISSION_GRANTED);
     }
 
     private List<AppInfo> getFiltersAppInfos() {
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
index 91a3106..27b9c18 100644
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -29,12 +29,13 @@
 import android.util.FloatProperty;
 import android.util.Log;
 
-import java.util.ArrayList;
-
 import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
+import java.util.ArrayList;
+
+
 /**
  * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or
  * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet.
@@ -137,11 +138,13 @@
 
         mProperty.switchToSpring();
 
-        mSpring.setStartVelocity(velocity);
-
         float startValue = end == 0 ? mValues[1] : mValues[0];
         float endValue = end == 0 ? mValues[0] : mValues[1];
-        mSpring.setStartValue(startValue);
+
+        // Ensures that the velocity matches the direction of the values.
+        velocity = Math.signum(endValue - startValue) * Math.abs(velocity);
+        mSpring.setStartVelocity(velocity);
+
         new Handler(Looper.getMainLooper()).postDelayed(() -> {
             mSpring.animateToFinalPosition(endValue);
         }, getStartDelay());
diff --git a/src/com/android/launcher3/graphics/BitmapCreationCheck.java b/src/com/android/launcher3/graphics/BitmapCreationCheck.java
new file mode 100644
index 0000000..e63e542
--- /dev/null
+++ b/src/com/android/launcher3/graphics/BitmapCreationCheck.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 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.graphics;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.Application;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewTreeObserver.OnDrawListener;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.GraphicsUtils;
+
+/**
+ * Utility class to check bitmap creation during draw pass.
+ */
+public class BitmapCreationCheck {
+
+    private static final String TAG = "BitmapCreationCheck";
+
+    public static final boolean ENABLED = false;
+
+    /**
+     * Starts tracking bitmap creations during {@link View#draw(Canvas)} calls
+     */
+    public static void startTracking(Context context) {
+        MyTracker tracker = new MyTracker();
+        ((Application) context.getApplicationContext()).registerActivityLifecycleCallbacks(tracker);
+        GraphicsUtils.sOnNewBitmapRunnable = tracker::onBitmapCreated;
+    }
+
+    @TargetApi(VERSION_CODES.Q)
+    private static class MyTracker
+            implements ActivityLifecycleCallbacks, OnAttachStateChangeListener {
+
+        private final ThreadLocal<Boolean> mCurrentThreadDrawing =
+                ThreadLocal.withInitial(() -> false);
+
+        @Override
+        public void onActivityCreated(Activity activity, Bundle bundle) {
+            activity.getWindow().getDecorView().addOnAttachStateChangeListener(this);
+        }
+
+        @Override
+        public void onActivityStarted(Activity activity) { }
+
+        @Override
+        public void onActivityResumed(Activity activity) { }
+
+        @Override
+        public void onActivityPaused(Activity activity) { }
+
+        @Override
+        public void onActivityStopped(Activity activity) { }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
+
+        @Override
+        public void onActivityDestroyed(Activity activity) { }
+
+        @Override
+        public void onViewAttachedToWindow(View view) {
+            view.getViewTreeObserver().addOnDrawListener(new MyViewDrawListener(view.getHandler()));
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View view) { }
+
+        private class MyViewDrawListener implements OnDrawListener, Runnable {
+
+            private final Handler mHandler;
+
+            MyViewDrawListener(Handler handler) {
+                mHandler = handler;
+            }
+
+            @Override
+            public void onDraw() {
+                mCurrentThreadDrawing.set(true);
+                Utilities.postAsyncCallback(mHandler, this);
+            }
+
+            @Override
+            public void run() {
+                mCurrentThreadDrawing.set(false);
+            }
+        }
+
+        private void onBitmapCreated() {
+            if (mCurrentThreadDrawing.get()) {
+                Log.e(TAG, "Bitmap created during draw pass", new Exception());
+            }
+        }
+    }
+
+}
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index f352b46..b004edf 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -44,7 +44,7 @@
 public class LoggerUtils {
     private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
     private static final String UNKNOWN = "UNKNOWN";
-    private static final int DEFAULT_PREDICTED_RANK = -100;
+    private static final int DEFAULT_PREDICTED_RANK = 10000;
 
     public static String getFieldName(int value, Class c) {
         SparseArray<String> cache;
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 48f1c49..21c5ac5 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -16,14 +16,10 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -176,33 +172,6 @@
         }
     }
 
-    public static final Factory<Launcher> DISMISS_PREDICTION = (launcher, itemInfo) -> {
-        if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
-        if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION
-                && itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
-            return null;
-        }
-        return new DismissPrediction(launcher, itemInfo);
-    };
-
-    public static class DismissPrediction extends SystemShortcut<Launcher> {
-        public DismissPrediction(Launcher launcher, ItemInfo itemInfo) {
-            super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label, launcher,
-                    itemInfo);
-        }
-
-        @Override
-        public void onClick(View view) {
-            PopupContainerWithArrow.closeAllOpenViews(mTarget);
-            mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
-                    ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
-            AppLaunchTracker.INSTANCE.get(view.getContext()).onDismissApp(
-                    mItemInfo.getTargetComponent(),
-                    mItemInfo.user,
-                    AppLaunchTracker.CONTAINER_PREDICTIONS);
-        }
-    }
-
     public static void dismissTaskMenuView(BaseDraggingActivity activity) {
         AbstractFloatingView.closeOpenViews(activity, true,
             AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 3ec480d..7ae0526 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -23,7 +23,7 @@
 import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_SCALE_COMPONENT;
 import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
 
 import android.animation.Animator;
@@ -380,6 +380,7 @@
 
         final LauncherState targetState;
         final float progress = mCurrentAnimation.getProgressFraction();
+        final float progressVelocity = velocity * mProgressMultiplier;
         final float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
         if (fling) {
             targetState =
@@ -406,7 +407,7 @@
                 startProgress = 1;
             } else {
                 startProgress = Utilities.boundToRange(progress
-                        + velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
+                        + progressVelocity * getSingleFrameMs(mLauncher), 0f, 1f);
                 duration = BaseSwipeDetector.calculateDuration(velocity,
                         endProgress - Math.max(progress, 0)) * durationMultiplier;
             }
@@ -421,7 +422,7 @@
                 startProgress = 0;
             } else {
                 startProgress = Utilities.boundToRange(progress
-                        + velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
+                        + progressVelocity * getSingleFrameMs(mLauncher), 0f, 1f);
                 duration = BaseSwipeDetector.calculateDuration(velocity,
                         Math.min(progress, 1) - endProgress) * durationMultiplier;
             }
@@ -433,8 +434,8 @@
         maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
         updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
                 targetState, velocity, fling);
-        mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, velocity);
-        if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+        mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, progressVelocity);
+        if (fling && targetState == LauncherState.ALL_APPS && !QUICKSTEP_SPRINGS.get()) {
             mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
         }
         anim.start();
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 0c9a28b..fa625ed 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -399,7 +399,8 @@
         Drawable drawable = null;
         Drawable badge = null;
         boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
-                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+                && !info.isDisabled(); // Use original icon for disabled icons.
         Drawable btvIcon = originalView instanceof BubbleTextView
                 ? ((BubbleTextView) originalView).getIcon() : null;
         if (info instanceof SystemShortcut) {
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
index c3186f6..b6c81ae 100644
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ b/src/com/android/launcher3/views/WorkEduView.java
@@ -52,8 +52,6 @@
     private static final int WORK_EDU_PERSONAL_APPS = 1;
     private static final int WORK_EDU_WORK_APPS = 2;
 
-    private static LauncherStateManager.StateListener sStateListener;
-
     private Rect mInsets = new Rect();
     private View mViewWrapper;
     private Button mProceedButton;
diff --git a/src/com/android/launcher3/views/WorkFooterContainer.java b/src/com/android/launcher3/views/WorkFooterContainer.java
index fb17b4f..f8add9a 100644
--- a/src/com/android/launcher3/views/WorkFooterContainer.java
+++ b/src/com/android/launcher3/views/WorkFooterContainer.java
@@ -15,32 +15,61 @@
  */
 package com.android.launcher3.views;
 
+import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.WorkModeSwitch;
+import com.android.launcher3.pm.UserCache;
 
 /**
  * Container to show work footer in all-apps.
  */
-public class WorkFooterContainer extends RelativeLayout {
+public class WorkFooterContainer extends LinearLayout implements Insettable {
+    private Rect mInsets = new Rect();
+
+    private WorkModeSwitch mWorkModeSwitch;
+    private TextView mWorkModeLabel;
+
+    protected final ObjectAnimator mOpenCloseAnimator;
 
     public WorkFooterContainer(Context context) {
-        super(context);
+        this(context, null, 0);
     }
 
     public WorkFooterContainer(Context context, AttributeSet attrs) {
-        super(context, attrs);
+        this(context, attrs, 0);
     }
 
     public WorkFooterContainer(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
     }
 
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
         updateTranslation();
+        this.setVisibility(shouldShowWorkFooter() ? VISIBLE : GONE);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mWorkModeSwitch = findViewById(R.id.work_mode_toggle);
+        mWorkModeLabel = findViewById(R.id.work_mode_label);
     }
 
     @Override
@@ -56,4 +85,44 @@
             setTranslationY(Math.max(0, availableBot - getBottom()));
         }
     }
+
+    @Override
+    public void setInsets(Rect insets) {
+        int bottomInset = insets.bottom - mInsets.bottom;
+        mInsets.set(insets);
+        setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
+                getPaddingBottom() + bottomInset);
+    }
+
+    /**
+     * Animates in/out work profile toggle panel based on the tab user is on
+     */
+    public void setWorkTabVisible(boolean workTabVisible) {
+        if (!shouldShowWorkFooter()) return;
+
+        mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(ALPHA, workTabVisible ? 1 : 0));
+        mOpenCloseAnimator.start();
+    }
+
+    /**
+     * Refreshes views based on current work profile enabled status
+     */
+    public void refresh() {
+        if (!shouldShowWorkFooter()) return;
+        boolean anyProfileQuietModeEnabled = UserCache.INSTANCE.get(
+                getContext()).isAnyProfileQuietModeEnabled();
+
+        mWorkModeLabel.setText(anyProfileQuietModeEnabled
+                ? R.string.work_mode_off_label : R.string.work_mode_on_label);
+        mWorkModeLabel.setCompoundDrawablesWithIntrinsicBounds(
+                anyProfileQuietModeEnabled ? R.drawable.ic_corp_off : R.drawable.ic_corp, 0, 0, 0);
+        mWorkModeSwitch.refresh();
+    }
+
+    private boolean shouldShowWorkFooter() {
+        Launcher launcher = Launcher.getLauncher(getContext());
+        return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
+                || launcher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+                == PackageManager.PERMISSION_GRANTED);
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index de9757f..4b1a067 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -41,6 +41,7 @@
 import com.android.launcher3.util.rule.ShellCommandRule;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,12 +71,14 @@
 
     @Test
     @PortraitLandscape
+    @Ignore // b/148867106
     public void testWidgetConfig() throws Throwable {
         runTest(true);
     }
 
     @Test
     @PortraitLandscape
+    @Ignore // b/148867106
     public void testConfigCancelled() throws Throwable {
         runTest(false);
     }
diff --git a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
index dcc7b7c..d6dfdd9 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
@@ -32,12 +32,62 @@
         return Pattern.compile(regex).matcher(string).find();
     }
 
+    static class LogcatMatch {
+        String logcatPattern;
+        int bug;
+
+        LogcatMatch(String logcatPattern, int bug) {
+            this.logcatPattern = logcatPattern;
+            this.bug = bug;
+        }
+    }
+
+    static class ExceptionMatch {
+        String exceptionPattern;
+        LogcatMatch[] logcatMatches;
+
+        ExceptionMatch(String exceptionPattern, LogcatMatch[] logcatMatches) {
+            this.exceptionPattern = exceptionPattern;
+            this.logcatMatches = logcatMatches;
+        }
+    }
+
+    private static final ExceptionMatch[] EXCEPTION_MATCHES = {
+            new ExceptionMatch(
+                    "java.lang.AssertionError: http://go/tapl : Tests are broken by a "
+                            + "non-Launcher system error: Phone is locked",
+                    new LogcatMatch[]{
+                            new LogcatMatch(
+                                    "BroadcastQueue: Can't deliver broadcast to com.android"
+                                            + ".systemui.*Crashing it",
+                                    147845913),
+                            new LogcatMatch(
+                                    "Attempt to invoke virtual method 'boolean android\\"
+                                            + ".graphics\\.Bitmap\\.isRecycled\\(\\)' on a null "
+                                            + "object reference",
+                                    148424291),
+                            new LogcatMatch(
+                                    "java\\.lang\\.IllegalArgumentException\\: Ranking map "
+                                            + "doesn't contain key",
+                                    148570537),
+                    }),
+            new ExceptionMatch("Launcher didn't initialize",
+                    new LogcatMatch[]{
+                            new LogcatMatch(
+                                    "ActivityManager: Reason: executing service com.google"
+                                            + ".android.apps.nexuslauncher/com.android.launcher3"
+                                            + ".notification.NotificationListener",
+                                    148238677),
+                    }),
+    };
+
     static int getBugForFailure(CharSequence exception) {
         if ("com.google.android.setupwizard".equals(
                 UiDevice.getInstance(getInstrumentation()).getLauncherPackageName())) {
             return 145935261;
         }
 
+
         final String logSinceBoot;
         try {
             final String systemBootTime =
@@ -51,39 +101,14 @@
             return 0;
         }
 
-        if (matches(
-                "java.lang.AssertionError: http://go/tapl : Tests are broken by a non-Launcher "
-                        + "system error: Phone is locked",
-                exception)) {
-            if (matches(
-                    "BroadcastQueue: Can't deliver broadcast to com.android.systemui.*Crashing it",
-                    logSinceBoot)) {
-                return 147845913;
-            }
-            if (matches(
-                    "Attempt to invoke virtual method 'boolean android\\.graphics\\.Bitmap\\"
-                            + ".isRecycled\\(\\)' on a null object reference",
-                    logSinceBoot)) {
-                return 148424291;
-            }
-            if (matches(
-                    "java\\.lang\\.IllegalArgumentException\\: Ranking map doesn't contain key",
-                    logSinceBoot)) {
-                return 148570537;
-            }
-        } else if (matches("java.lang.AssertionError: Launcher build match not found", exception)) {
-            if (matches(
-                    "TestStabilityRule: Launcher package: com.google.android.setupwizard",
-                    logSinceBoot)) {
-                return 145935261;
-            }
-        } else if (matches("Launcher didn't initialize", exception)) {
-            if (matches(
-                    "ActivityManager: Reason: executing service com.google.android.apps"
-                            + ".nexuslauncher/com.android.launcher3.notification"
-                            + ".NotificationListener",
-                    logSinceBoot)) {
-                return 148238677;
+        for (ExceptionMatch exceptionMatch : EXCEPTION_MATCHES) {
+            if (matches(exceptionMatch.exceptionPattern, exception)) {
+                for (LogcatMatch logcatMatch : exceptionMatch.logcatMatches) {
+                    if (matches(logcatMatch.logcatPattern, logSinceBoot)) {
+                        return logcatMatch.bug;
+                    }
+                }
+                break;
             }
         }
 
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 7763cc2..02d07bb 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -78,10 +78,14 @@
                 try {
                     base.evaluate();
                 } catch (Throwable e) {
-                    if (!Log.getStackTraceString(e).contains(
+                    final String stackTrace = Log.getStackTraceString(e);
+                    if (!stackTrace.contains(
                             "androidx.test.internal.runner.junit4.statement.RunBefores.evaluate")) {
                         // Test failed to deinitialize. Since the global state is probably
                         // corrupted, won't execute other tests.
+                        Log.d(TAG,
+                                "Detected an exception from test finalizer, will skip further "
+                                        + "tests: " + stackTrace);
                         sHadFailedTestDeinitialization = true;
                     }
                     throw e;
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index 1feb4e7..61f5e05 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -73,8 +73,7 @@
             return new Statement() {
                 @Override
                 public void evaluate() throws Throwable {
-                    if (sRunFlavor == 0) sRunFlavor = getRunFlavor();
-                    if ((stability.flavors() & sRunFlavor) != 0) {
+                    if ((stability.flavors() & getRunFlavor()) != 0) {
                         Log.d(TAG, "Running " + description.getDisplayName());
                         base.evaluate();
                     } else {
@@ -87,7 +86,9 @@
         }
     }
 
-    private static int getRunFlavor() {
+    public static int getRunFlavor() {
+        if (sRunFlavor != 0) return sRunFlavor;
+
         final String flavorOverride = InstrumentationRegistry.getArguments().getString("flavor");
 
         if (flavorOverride != null) {
@@ -130,34 +131,32 @@
             throw new AssertionError("Platform build match not found");
         }
 
-        final int runFlavor;
-
         if (launcherBuildMatcher.group("local") != null && (
                 platformBuildMatcher.group("commandLine") != null ||
                         platformBuildMatcher.group("postsubmit") != null)) {
             Log.d(TAG, "LOCAL RUN");
-            runFlavor = LOCAL;
+            sRunFlavor = LOCAL;
         } else if (launcherBuildMatcher.group("presubmit") != null
                 && platformBuildMatcher.group("postsubmit") != null) {
             Log.d(TAG, "UNBUNDLED PRESUBMIT");
-            runFlavor = UNBUNDLED_PRESUBMIT;
+            sRunFlavor = UNBUNDLED_PRESUBMIT;
         } else if (launcherBuildMatcher.group("postsubmit") != null
                 && platformBuildMatcher.group("postsubmit") != null) {
             Log.d(TAG, "UNBUNDLED POSTSUBMIT");
-            runFlavor = UNBUNDLED_POSTSUBMIT;
+            sRunFlavor = UNBUNDLED_POSTSUBMIT;
         } else if (launcherBuildMatcher.group("platform") != null
                 && platformBuildMatcher.group("presubmit") != null) {
             Log.d(TAG, "PLATFORM PRESUBMIT");
-            runFlavor = PLATFORM_PRESUBMIT;
+            sRunFlavor = PLATFORM_PRESUBMIT;
         } else if (launcherBuildMatcher.group("platform") != null
                 && (platformBuildMatcher.group("postsubmit") != null
                 || platformBuildMatcher.group("commandLine") != null)) {
             Log.d(TAG, "PLATFORM POSTSUBMIT");
-            runFlavor = PLATFORM_POSTSUBMIT;
+            sRunFlavor = PLATFORM_POSTSUBMIT;
         } else {
             throw new AssertionError("Unrecognized run flavor");
         }
 
-        return runFlavor;
+        return sRunFlavor;
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 59d295f..9f29a1a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -25,6 +25,7 @@
 import android.view.MotionEvent;
 
 import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiObject2;
 
 import com.android.launcher3.testing.TestProtocol;
@@ -179,9 +180,12 @@
                     endX = startX;
                     endY = 0;
                 }
+                final boolean launcherIsVisible =
+                        mLauncher.hasLauncherObject(By.textStartsWith(""));
+                final boolean isZeroButton = mLauncher.getNavigationModel()
+                        == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
                 mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState,
-                        mLauncher.getNavigationModel()
-                                == LauncherInstrumentation.NavigationModel.ZERO_BUTTON
+                        launcherIsVisible && isZeroButton
                                 ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
                                 : LauncherInstrumentation.GestureScope.OUTSIDE
                 );
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 5555eab..4089897 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -106,6 +106,7 @@
     private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
     private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
     private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL");
+    private static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
 
     // Types for launcher containers that the user is interacting with. "Background" is a
     // pseudo-container corresponding to inactive launcher covered by another app.
@@ -633,6 +634,15 @@
                 log(action = "clicking home button from " + getVisibleStateMessage());
                 try (LauncherInstrumentation.Closable c = addContextLayer(action)) {
                     mDevice.waitForIdle();
+
+                    if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
+                        if (hasLauncherObject(CONTEXT_MENU_RES_ID) ||
+                                hasLauncherObject(WIDGETS_RES_ID)
+                                        && !mDevice.isNaturalOrientation()) {
+                            expectEvent(EVENT_PILFER_POINTERS);
+                        }
+                    }
+
                     runToState(
                             waitForSystemUiObject("home")::click,
                             NORMAL_STATE_ORDINAL,
@@ -797,7 +807,7 @@
         return mDevice.hasObject(getLauncherObjectSelector(resId));
     }
 
-    private boolean hasLauncherObject(BySelector selector) {
+    boolean hasLauncherObject(BySelector selector) {
         return mDevice.hasObject(makeLauncherSelector(selector));
     }
 
@@ -1051,16 +1061,21 @@
 
     void sendPointer(long downTime, long currentTime, int action, Point point,
             GestureScope gestureScope) {
-        if (gestureScope != GestureScope.OUTSIDE) {
-            switch (action) {
-                case MotionEvent.ACTION_DOWN:
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                if (gestureScope != GestureScope.OUTSIDE) {
                     expectEvent(EVENT_TOUCH_DOWN);
-                    break;
-                case MotionEvent.ACTION_UP:
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (gestureScope != GestureScope.INSIDE) {
+                    expectEvent(EVENT_PILFER_POINTERS);
+                }
+                if (gestureScope != GestureScope.OUTSIDE) {
                     expectEvent(gestureScope == GestureScope.INSIDE
                             ? EVENT_TOUCH_UP : EVENT_TOUCH_CANCEL);
-                    break;
-            }
+                }
+                break;
         }
 
         final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
@@ -1226,6 +1241,12 @@
     }
 
     Closable eventsCheck() {
+        if ("com.android.launcher3".equals(getLauncherPackageName())) {
+            // Not checking specific Launcher3 event sequences.
+            return () -> {
+            };
+        }
+
         // Entering events check block.
         startRecordingEvents();