Merge "Enable FallbackRecentsTest" into ub-launcher3-rvc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 7e8e51e..d1185bd 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -54,6 +54,7 @@
     ShortcutsContainer shortcuts_container = 8;
     SettingsContainer settings_container = 9;
     PredictedHotseatContainer predicted_hotseat_container = 10;
+    TaskSwitcherContainer task_switcher_container = 11;
   }
 }
 
@@ -82,6 +83,9 @@
 message SettingsContainer {
 }
 
+message TaskSwitcherContainer {
+}
+
 enum Attribute {
   UNKNOWN = 0;
   DEFAULT_LAYOUT = 1;       // icon automatically placed in workspace, folder, hotseat
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index d5b0687..fc0dcd5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -20,6 +20,7 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.views.RecentsView;
 
@@ -56,6 +57,17 @@
         return 1.0f;
     }
 
+    @Override
+    public void onBackPressed(Launcher launcher) {
+        launcher.getStateManager().goToState(LauncherState.OVERVIEW);
+        RecentsView recentsView = launcher.<RecentsView>getOverviewPanel();
+        if (recentsView != null) {
+            recentsView.resetModalVisuals();
+        } else {
+            super.onBackPressed(launcher);
+        }
+    }
+
     public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
         Rect out = new Rect();
         activity.<RecentsView>getOverviewPanel().getTaskSize(out);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index b5fb31a..f5c5874 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -32,7 +32,6 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.VibratorWrapper;
@@ -96,8 +95,8 @@
      * depend on proper class initialization.
      */
     protected void initAfterSubclassConstructor() {
-        initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
-                .getDeviceProfile(mContext));
+        initTransitionEndpoints(
+                mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
     }
 
     protected void performHapticFeedback() {
@@ -144,13 +143,14 @@
                 TaskView nextTask = mRecentsView.getTaskView(taskId);
                 if (nextTask != null) {
                     mGestureState.updateLastStartedTaskId(taskId);
+                    boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
+                            .contains(taskId);
                     nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
                             success -> {
                                 resultCallback.accept(success);
                                 if (success) {
-                                    if (mRecentsView.indexOfChild(nextTask)
-                                            == getLastAppearedTaskIndex()) {
-                                        onRestartLastAppearedTask();
+                                    if (hasTaskPreviouslyAppeared) {
+                                        onRestartPreviouslyAppearedTask();
                                     }
                                 } else {
                                     mActivityInterface.onLaunchTaskFailed();
@@ -171,7 +171,7 @@
      * start A again to ensure it stays on top.
      */
     @CallSuper
-    protected void onRestartLastAppearedTask() {
+    protected void onRestartPreviouslyAppearedTask() {
         // Finish the controller here, since we won't get onTaskAppeared() for a task that already
         // appeared.
         if (mRecentsAnimationController != null) {
@@ -205,7 +205,7 @@
         mRecentsAnimationController = recentsAnimationController;
         mRecentsAnimationTargets = targets;
         mTransformParams.setTargetSet(mRecentsAnimationTargets);
-        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
+        DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
         RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
                 mGestureState.getRunningTaskId());
 
@@ -300,8 +300,7 @@
             if (TestProtocol.sDebugTracing) {
                 Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
             }
-            initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
-                .getDeviceProfile(mContext));
+            initTransitionEndpoints(createdActivity.getDeviceProfile());
         }
         return true;
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
index 414d7ae..e825c5f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
@@ -131,7 +131,7 @@
 
     private static final int STATE_CAPTURE_SCREENSHOT =
             getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT");
-    private static final int STATE_SCREENSHOT_CAPTURED =
+    protected static final int STATE_SCREENSHOT_CAPTURED =
             getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED");
     private static final int STATE_SCREENSHOT_VIEW_SHOWN =
             getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN");
@@ -1122,8 +1122,8 @@
     }
 
     @Override
-    protected void onRestartLastAppearedTask() {
-        super.onRestartLastAppearedTask();
+    protected void onRestartPreviouslyAppearedTask() {
+        super.onRestartPreviouslyAppearedTask();
         reset();
     }
 
@@ -1192,7 +1192,7 @@
         mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
     }
 
-    private void switchToScreenshot() {
+    protected void switchToScreenshot() {
         final int runningTaskId = mGestureState.getRunningTaskId();
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (mRecentsAnimationController != null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index 96913c6..fc7a119 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -111,6 +111,16 @@
     }
 
     @Override
+    protected void switchToScreenshot() {
+        if (mRunningOverHome) {
+            // When the current task is home, then we don't need to capture anything
+            mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+        } else {
+            super.switchToScreenshot();
+        }
+    }
+
+    @Override
     protected void notifyGestureAnimationStartToRecents() {
         if (mRunningOverHome) {
             mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 852a51a..1701020 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -139,13 +139,12 @@
      */
     protected DeviceProfile createDeviceProfile() {
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
-        DeviceProfile dp1 = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
 
         // In case we are reusing IDP, create a copy so that we don't conflict with Launcher
         // activity.
         return (mRecentsRootView != null) && isInMultiWindowMode()
                 ? dp.getMultiWindowProfile(this, getMultiWindowDisplaySize())
-                : dp1.copy(this);
+                : dp.copy(this);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
index b17730b..dc8f1c5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -83,15 +83,15 @@
         mGestureState = gestureState;
         mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
         mTransformParams = transformParams;
+
+        mTaskViewSimulator.setLayoutRotation(
+                mDeviceState.getCurrentActiveRotation(), mDeviceState.getDisplayRotation());
     }
 
     protected void initTransitionEndpoints(DeviceProfile dp) {
         mDp = dp;
 
         mTaskViewSimulator.setDp(dp);
-        mTaskViewSimulator.setLayoutRotation(
-                mDeviceState.getCurrentActiveRotation(),
-                mDeviceState.getDisplayRotation());
         mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
                 dp, mContext, TEMP_RECT,
                 mTaskViewSimulator.getOrientationState().getOrientationHandler());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index a6a08cb..177f9a0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -179,6 +179,12 @@
         }
 
         /**
+         * Called when the system wants to reset the modal visuals.
+         */
+        public void resetModalVisuals() {
+        }
+
+        /**
          * Gets the modal state system shortcut.
          */
         public SystemShortcut getModalStateSystemShortcut(WorkspaceItemInfo itemInfo) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
index ea1795c..4eae437 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
@@ -26,23 +26,18 @@
 
 import android.app.Activity;
 import android.app.ActivityOptions;
-import android.content.ComponentName;
-import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.UserHandle;
 import android.view.View;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
 import com.android.launcher3.model.WellbeingModel;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.popup.SystemShortcut.AppInfo;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -71,28 +66,7 @@
 
     SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
 
-    static WorkspaceItemInfo dummyInfo(TaskView view) {
-        Task task = view.getTask();
-
-        WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo(){
-            /**
-             * Helps to log events as {@link LauncherAtom.Task}
-             * instead of {@link LauncherAtom.ItemInfo}.
-             */
-            @Override
-            public LauncherAtom.ItemInfo buildProto() {
-                return view.buildProto();
-            }
-        };
-        dummyInfo.intent = new Intent();
-        ComponentName component = task.getTopComponent();
-        dummyInfo.getIntent().setComponent(component);
-        dummyInfo.user = UserHandle.of(task.key.userId);
-        dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
-        return dummyInfo;
-    }
-
-    TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, dummyInfo(view));
+    TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, view.getItemInfo());
 
     abstract class MultiWindowFactory implements TaskShortcutFactory {
 
@@ -136,7 +110,7 @@
 
         public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity,
                 TaskView taskView, MultiWindowFactory factory, LauncherEvent launcherEvent) {
-            super(iconRes, textRes, activity, dummyInfo(taskView));
+            super(iconRes, textRes, activity, taskView.getItemInfo());
             mLauncherEvent = launcherEvent;
             mHandler = new Handler(Looper.getMainLooper());
             mTaskView = taskView;
@@ -222,7 +196,7 @@
                 WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
                         future, animStartedListener, mHandler, true /* scaleUp */,
                         taskKey.displayId);
-                mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.buildProto());
+                mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.getItemInfo());
             }
         }
     }
@@ -306,7 +280,7 @@
         private final TaskView mTaskView;
 
         public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
-            super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, dummyInfo(tv));
+            super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, tv.getItemInfo());
             mTaskView = tv;
         }
 
@@ -323,29 +297,29 @@
             mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
             dismissTaskMenuView(mTarget);
             mTarget.getStatsLogManager().log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP,
-                    mTaskView.buildProto());
+                    mTaskView.getItemInfo());
         }
     }
 
     TaskShortcutFactory INSTALL = (activity, view) ->
             InstantAppResolver.newInstance(activity).isInstantApp(activity,
                  view.getTask().getTopComponent().getPackageName())
-                    ? new SystemShortcut.Install(activity, dummyInfo(view)) : null;
+                    ? new SystemShortcut.Install(activity, view.getItemInfo()) : null;
 
     TaskShortcutFactory WELLBEING = (activity, view) ->
-            WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, dummyInfo(view));
+            WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, view.getItemInfo());
 
     TaskShortcutFactory SCREENSHOT = (activity, tv) -> {
         if (ENABLE_OVERVIEW_ACTIONS.get()) {
             return tv.getThumbnail().getTaskOverlay()
-                .getScreenshotShortcut(activity, dummyInfo(tv));
+                .getScreenshotShortcut(activity, tv.getItemInfo());
         }
         return null;
     };
 
     TaskShortcutFactory MODAL = (activity, tv) -> {
         if (ENABLE_OVERVIEW_ACTIONS.get() && ENABLE_OVERVIEW_SELECTIONS.get()) {
-            return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(dummyInfo(tv));
+            return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(tv.getItemInfo());
         }
         return null;
     };
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 4ca4e4c..37314ea 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -539,6 +539,8 @@
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
             gestureState.updateRunningTask(mGestureState.getRunningTask());
             gestureState.updateLastStartedTaskId(mGestureState.getLastStartedTaskId());
+            gestureState.updatePreviouslyAppearedTaskIds(
+                    mGestureState.getPreviouslyAppearedTaskIds());
         } else {
             gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
                     () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 05f4e91..873c672 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -30,6 +30,7 @@
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -1347,7 +1348,7 @@
             mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                     endState.logAction, Direction.UP, index, compKey);
             mActivity.getStatsLogManager().log(
-                    LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.buildProto());
+                    LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.getItemInfo());
         }
     }
 
@@ -1731,7 +1732,7 @@
 
     private void updatePageOffsets() {
         float offset = mAdjacentPageOffset * getWidth();
-        float modalOffset = mTaskModalness * getWidth();
+        float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness) * getWidth();
         if (mIsRtl) {
             offset = -offset;
             modalOffset = -modalOffset;
@@ -1759,6 +1760,16 @@
         return Math.max(getWidth(), 1);
     }
 
+    /**
+     * Resets the visuals when exit modal state.
+     */
+    public void resetModalVisuals() {
+        TaskView taskView = getCurrentPageTaskView();
+        if (taskView != null) {
+            taskView.getThumbnail().getTaskOverlay().resetModalVisuals();
+        }
+    }
+
     private void updateDeadZoneRects() {
         // Get the deadzone rect surrounding the clear all button to not dismiss overview to home
         mClearAllButtonDeadZoneRect.setEmpty();
@@ -1930,7 +1941,7 @@
                             endState.logAction, Direction.DOWN, indexOfChild(tv),
                             TaskUtils.getLaunchComponentKeyForTask(task.key));
                     mActivity.getStatsLogManager().log(
-                            LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.buildProto());
+                            LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.getItemInfo());
                 }
             } else {
                 onTaskLaunched(false);
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 e25c85b..3b1210e 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
@@ -40,6 +40,7 @@
 import android.animation.ValueAnimator;
 import android.app.ActivityOptions;
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.Outline;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -48,7 +49,6 @@
 import android.graphics.drawable.InsetDrawable;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Process;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -61,13 +61,14 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
@@ -213,7 +214,7 @@
             mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
                     Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
                     TaskUtils.getLaunchComponentKeyForTask(getTask().key));
-            mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, buildProto());
+            mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, getItemInfo());
         });
 
         mCurrentFullscreenParams = new FullscreenDrawParams(context);
@@ -226,14 +227,16 @@
     /**
      * Builds proto for logging
      */
-    public LauncherAtom.ItemInfo buildProto() {
+    public WorkspaceItemInfo getItemInfo() {
         ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(getTask().key);
-        LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
-        itemBuilder.setIsWork(componentKey.user != Process.myUserHandle());
-        itemBuilder.setTask(LauncherAtom.Task.newBuilder()
-                .setComponentName(componentKey.componentName.flattenToShortString())
-                .setIndex(getRecentsView().indexOfChild(this)));
-        return itemBuilder.build();
+        WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
+        dummyInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
+        dummyInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
+        dummyInfo.user = componentKey.user;
+        dummyInfo.intent = new Intent().setComponent(componentKey.componentName);
+        dummyInfo.title = TaskUtils.getTitle(getContext(), getTask());
+        dummyInfo.screenId = getRecentsView().indexOfChild(this);
+        return dummyInfo;
     }
 
     @Override
@@ -429,7 +432,7 @@
             getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
         } else {
             mMenuView = TaskMenuView.showForTask(this);
-            mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, buildProto());
+            mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, getItemInfo());
             UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
                     LauncherLogProto.ItemType.TASK_ICON);
             if (mMenuView != null) {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 188072a..00b5eb9 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -30,6 +30,8 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Manages the state for an active system gesture, listens for events from the system and Launcher,
@@ -128,6 +130,7 @@
     private ActivityManager.RunningTaskInfo mRunningTask;
     private GestureEndTarget mEndTarget;
     private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+    private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
     private int mLastStartedTaskId = -1;
 
     public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
@@ -147,6 +150,7 @@
         mRunningTask = other.mRunningTask;
         mEndTarget = other.mEndTarget;
         mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
+        mPreviouslyAppearedTaskIds = other.mPreviouslyAppearedTaskIds;
         mLastStartedTaskId = other.mLastStartedTaskId;
     }
 
@@ -234,6 +238,9 @@
      */
     public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
         mLastAppearedTaskTarget = lastAppearedTaskTarget;
+        if (lastAppearedTaskTarget != null) {
+            mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId);
+        }
     }
 
     /**
@@ -243,6 +250,14 @@
         return mLastAppearedTaskTarget != null ? mLastAppearedTaskTarget.taskId : -1;
     }
 
+    public void updatePreviouslyAppearedTaskIds(Set<Integer> previouslyAppearedTaskIds) {
+        mPreviouslyAppearedTaskIds = previouslyAppearedTaskIds;
+    }
+
+    public Set<Integer> getPreviouslyAppearedTaskIds() {
+        return mPreviouslyAppearedTaskIds;
+    }
+
     /**
      * Updates the last task that we started via startActivityFromRecents() during this gesture.
      */
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 390330f..a88ba3c 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -64,6 +64,9 @@
     private static Context sContext;
 
     private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
+    // LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
+    // from nano to lite, bake constant to prevent robo test failure.
+    private static final int DEFAULT_PAGE_INDEX = -2;
     private static final int FOLDER_HIERARCHY_OFFSET = 100;
 
     public StatsLogCompatManager(Context context) {
@@ -75,7 +78,7 @@
      */
     @Override
     public void log(EventEnum event) {
-        log(event, DEFAULT_INSTANCE_ID, LauncherAtom.ItemInfo.getDefaultInstance());
+        log(event, DEFAULT_INSTANCE_ID, null);
     }
 
     /**
@@ -83,31 +86,27 @@
      */
     @Override
     public void log(EventEnum event, InstanceId instanceId) {
-        log(event, instanceId, LauncherAtom.ItemInfo.getDefaultInstance());
+        log(event, instanceId, null);
     }
 
     /**
      * Logs an event and accompanying {@link ItemInfo}.
      */
     @Override
-    public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo info) {
+    public void log(EventEnum event, @Nullable ItemInfo info) {
         log(event, DEFAULT_INSTANCE_ID, info);
     }
 
-    @Override
-    public void log(EventEnum event, ItemInfo itemInfo) {
-        logInternal(event, DEFAULT_INSTANCE_ID, itemInfo);
-    }
-
     /**
      * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
      */
     @Override
     public void log(EventEnum event, InstanceId instanceId,
-            @Nullable LauncherAtom.ItemInfo info) {
+            @Nullable ItemInfo info) {
         logInternal(event, instanceId, info,
                 LAUNCHER_UICHANGED__DST_STATE__HOME,
-                LAUNCHER_UICHANGED__DST_STATE__BACKGROUND);
+                LAUNCHER_UICHANGED__DST_STATE__BACKGROUND,
+                DEFAULT_PAGE_INDEX);
     }
 
     /**
@@ -129,58 +128,60 @@
      */
     @Override
     public void log(EventEnum event, int srcState, int dstState, int pageIndex) {
-        LauncherAtom.ItemInfo info = LauncherAtom.ItemInfo.getDefaultInstance();
-        if (srcState == LAUNCHER_UICHANGED__DST_STATE__HOME
-                || dstState == LAUNCHER_UICHANGED__SRC_STATE__HOME) {
-            info = LauncherAtom.ItemInfo.newBuilder().setContainerInfo(
-                    LauncherAtom.ContainerInfo.newBuilder().setWorkspace(
-                            LauncherAtom.WorkspaceContainer.newBuilder().setPageIndex(pageIndex)
-                    )).build();
-        }
-        logInternal(event, DEFAULT_INSTANCE_ID, info, srcState, dstState);
+        logInternal(event, DEFAULT_INSTANCE_ID, null, srcState, dstState, pageIndex);
     }
 
-    private void logInternal(EventEnum event, InstanceId instanceId, @Nullable ItemInfo info) {
+    /**
+     * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
+     */
+    private void logInternal(EventEnum event, InstanceId instanceId,
+            @Nullable ItemInfo info, int srcState, int dstState, int pageIndex) {
+
         LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
                 new BaseModelUpdateTask() {
                     @Override
                     public void execute(LauncherAppState app, BgDataModel dataModel,
                             AllAppsList apps) {
-                        LauncherAtom.ItemInfo atomInfo = LauncherAtom.ItemInfo.getDefaultInstance();
-                        if (info != null) {
-                            if (info.container >= 0) {
-                                atomInfo = info.buildProto(dataModel.folders.get(info.container));
-                            } else {
-                                atomInfo = info.buildProto();
-                            }
-                        }
-                        logInternal(event, instanceId, atomInfo,
-                                LAUNCHER_UICHANGED__DST_STATE__HOME,
-                                LAUNCHER_UICHANGED__DST_STATE__BACKGROUND);
+                        writeEvent(event, instanceId, info, srcState, dstState, pageIndex,
+                                dataModel.folders);
                     }
                 });
     }
 
-    /**
-     * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
-     */
-    private void logInternal(EventEnum event, InstanceId instanceId,
-            @Nullable LauncherAtom.ItemInfo info, int srcState, int dstState) {
-        info = info == null ? LauncherAtom.ItemInfo.getDefaultInstance() : info;
+    private static void writeEvent(EventEnum event, InstanceId instanceId,
+            @Nullable ItemInfo info, int srcState, int dstState, int pageIndex,
+            IntSparseArrayMap<FolderInfo> folders) {
+
+        if (!Utilities.ATLEAST_R) {
+            return;
+        }
+        LauncherAtom.ItemInfo atomInfo = LauncherAtom.ItemInfo.getDefaultInstance();
+        if (info != null) {
+            if (info.container >= 0) {
+                atomInfo = info.buildProto(folders.get(info.container));
+            } else {
+                atomInfo = info.buildProto();
+            }
+        } else {
+            if (srcState == LAUNCHER_UICHANGED__DST_STATE__HOME
+                    || dstState == LAUNCHER_UICHANGED__SRC_STATE__HOME) {
+                atomInfo = LauncherAtom.ItemInfo.newBuilder().setContainerInfo(
+                        LauncherAtom.ContainerInfo.newBuilder().setWorkspace(
+                                LauncherAtom.WorkspaceContainer.newBuilder().setPageIndex(pageIndex)
+                        )).build();
+            }
+        }
 
         if (IS_VERBOSE) {
             String name = (event instanceof Enum) ? ((Enum) event).name() :
                     event.getId() + "";
 
             Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
-                    ? String.format("\n%s (State:%s->%s) \n%s", name, getStateString(srcState),
-                    getStateString(dstState), info)
-                    : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name,
-                            getStateString(srcState), getStateString(dstState), instanceId, info));
-        }
-
-        if (!Utilities.ATLEAST_R) {
-            return;
+                    ? String.format("\n%s (State:%s->%s) \n%s\n%s", name, getStateString(srcState),
+                            getStateString(dstState), info, atomInfo)
+                    : String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s\n%s", name,
+                            getStateString(srcState), getStateString(dstState), instanceId, info,
+                            atomInfo));
         }
 
         SysUiStatsLog.write(
@@ -191,24 +192,24 @@
                 null /* launcher extensions, deprecated */,
                 false /* quickstep_enabled, deprecated */,
                 event.getId() /* event_id */,
-                info.getItemCase().getNumber() /* target_id */,
+                atomInfo.getItemCase().getNumber() /* target_id */,
                 instanceId.getId() /* instance_id TODO */,
                 0 /* uid TODO */,
-                getPackageName(info) /* package_name */,
-                getComponentName(info) /* component_name */,
-                getGridX(info, false) /* grid_x */,
-                getGridY(info, false) /* grid_y */,
-                getPageId(info, false) /* page_id */,
-                getGridX(info, true) /* grid_x_parent */,
-                getGridY(info, true) /* grid_y_parent */,
-                getPageId(info, true) /* page_id_parent */,
-                getHierarchy(info) /* hierarchy */,
-                info.getIsWork() /* is_work_profile */,
-                info.getRank() /* rank */,
-                info.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
-                info.getFolderIcon().getToLabelState().getNumber() /* toState */,
-                info.getFolderIcon().getLabelInfo() /* edittext */,
-                getCardinality(info) /* cardinality */);
+                getPackageName(atomInfo) /* package_name */,
+                getComponentName(atomInfo) /* component_name */,
+                getGridX(atomInfo, false) /* grid_x */,
+                getGridY(atomInfo, false) /* grid_y */,
+                getPageId(atomInfo, false) /* page_id */,
+                getGridX(atomInfo, true) /* grid_x_parent */,
+                getGridY(atomInfo, true) /* grid_y_parent */,
+                getPageId(atomInfo, true) /* page_id_parent */,
+                getHierarchy(atomInfo) /* hierarchy */,
+                atomInfo.getIsWork() /* is_work_profile */,
+                atomInfo.getRank() /* rank */,
+                atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
+                atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
+                atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
+                getCardinality(atomInfo) /* cardinality */);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 5af3d70..8bd2281 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -26,8 +26,8 @@
 import static com.android.launcher3.logging.LoggerUtils.extractObjectNameAndAddress;
 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
+
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.content.ContentResolver;
@@ -49,6 +49,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
@@ -526,4 +527,15 @@
                 + " mFlags=" + mFlags
                 + "]";
     }
+
+    /**
+     * Returns the device profile based on expected launcher rotation
+     */
+    public DeviceProfile getLauncherDeviceProfile() {
+        InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
+        // TODO also check the natural orientation is landscape or portrait
+        return  (mLauncherRotation == ROTATION_90 || mLauncherRotation == ROTATION_270)
+                ? idp.landscapeProfile
+                : idp.portraitProfile;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index f5498c9..bec3050 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -23,6 +23,7 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
 
 import android.content.Context;
 import android.graphics.Canvas;
@@ -187,6 +188,7 @@
             mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
         }
         updateColors();
+        updateSysUiColors();
         updateDragHandleAlpha();
         invalidate();
     }
@@ -241,6 +243,18 @@
     }
 
     @Override
+    protected void updateSysUiColors() {
+        // Use a light system UI (dark icons) if all apps is behind at least half of the
+        // status bar.
+        boolean forceChange = mShelfTop <= mLauncher.getDeviceProfile().getInsets().top / 2f;
+        if (forceChange) {
+            mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
+        } else {
+            mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
+        }
+    }
+
+    @Override
     protected boolean shouldDragHandleBeVisible() {
         boolean needsAllAppsEdu = mIsTwoZoneSwipeModel
                 && !mOnboardingPrefs.hasReachedMaxCount(OnboardingPrefs.ALL_APPS_COUNT);
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2efa66f..935bb40 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -172,8 +172,10 @@
     <string name="folder_closed">Folder closed</string>
     <!-- Folder renamed format -->
     <string name="folder_renamed">Folder renamed to <xliff:g id="name" example="Games">%1$s</xliff:g></string>
-    <!-- Folder name format -->
-    <string name="folder_name_format">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g></string>
+    <!-- Folder name format when folder has less than 4 items -->
+    <string name="folder_name_format_exact">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> items</string>
+    <!-- Folder name format when folder has 4 or more items shown in preview-->
+    <string name="folder_name_format_overflow">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> or more items</string>
 
     <!-- Strings for the customization mode -->
     <!-- Text for widget add button -->
diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index 7ca416d..8f3a83e 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -90,6 +90,22 @@
     }
 
     @Test
+    public void testCustomProfileLoaded_with_folder_custom_title() throws Exception {
+        writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0).putFolder("CustomFolder")
+                .addApp(TEST_PACKAGE, TEST_PACKAGE)
+                .addApp(TEST_PACKAGE, TEST_PACKAGE)
+                .addApp(TEST_PACKAGE, TEST_PACKAGE)
+                .build());
+
+        // Verify folder
+        assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
+        ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
+        assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
+        assertEquals(3, ((FolderInfo) info).contents.size());
+        assertEquals("CustomFolder", info.title.toString());
+    }
+
+    @Test
     public void testCustomProfileLoaded_with_widget() throws Exception {
         String pendingAppPkg = "com.test.pending";
 
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java b/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
index d3659eb..4e21dce 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
@@ -47,6 +47,7 @@
     private static final String ATTR_PACKAGE_NAME = "packageName";
     private static final String ATTR_CLASS_NAME = "className";
     private static final String ATTR_TITLE = "title";
+    private static final String ATTR_TITLE_TEXT = "titleText";
     private static final String ATTR_SCREEN = "screen";
 
     // x and y can be specified as negative integers, in which case -1 represents the
@@ -145,8 +146,17 @@
         }
 
         public FolderBuilder putFolder(int titleResId) {
-            FolderBuilder folderBuilder = new FolderBuilder();
             items.put(ATTR_TITLE, Integer.toString(titleResId));
+            return putFolder();
+        }
+
+        public FolderBuilder putFolder(String title) {
+            items.put(ATTR_TITLE_TEXT, title);
+            return putFolder();
+        }
+
+        private FolderBuilder putFolder() {
+            FolderBuilder folderBuilder = new FolderBuilder();
             items.put(ATTR_CHILDREN, folderBuilder.mChildren);
             mNodes.add(Pair.create(TAG_FOLDER, items));
             return folderBuilder;
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 5971a02..432073e 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -132,6 +132,7 @@
     private static final String ATTR_PACKAGE_NAME = "packageName";
     private static final String ATTR_CLASS_NAME = "className";
     private static final String ATTR_TITLE = "title";
+    private static final String ATTR_TITLE_TEXT = "titleText";
     private static final String ATTR_SCREEN = "screen";
 
     // x and y can be specified as negative integers, in which case -1 represents the
@@ -585,7 +586,8 @@
             if (titleResId != 0) {
                 title = mSourceRes.getString(titleResId);
             } else {
-                title = "";
+                String titleText = getAttributeValue(parser, ATTR_TITLE_TEXT);
+                title = TextUtils.isEmpty(titleText) ? "" : titleText;
             }
 
             mValues.put(Favorites.TITLE, title);
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 268b910..88dbfd6 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -188,7 +188,7 @@
             }
             getUserEventDispatcher().logAppLaunch(v, intent, user);
             if (item != null) {
-                getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, item.buildProto());
+                getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, item);
             }
             return true;
         } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 208d565..5512654 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -163,6 +163,7 @@
         public static final int CONTAINER_SEARCH_RESULTS = -106;
         public static final int CONTAINER_SHORTCUTS = -107;
         public static final int CONTAINER_SETTINGS = -108;
+        public static final int CONTAINER_TASKSWITCHER = -109;
 
         public static final String containerToString(int container) {
             switch (container) {
@@ -250,6 +251,12 @@
         public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
 
         /**
+         * Type of the item is recents task.
+         * TODO(hyunyoungs): move constants not related to Favorites DB to a better location.
+         */
+        public static final int ITEM_TYPE_TASK = 7;
+
+        /**
          * The appWidgetId of the widget
          *
          * <P>Type: INTEGER</P>
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f6c392b..4198e9f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -418,10 +418,7 @@
         mStatsLogManager.log(
                 LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED,
                 dragObject.logInstanceId,
-                dragObject.dragSource instanceof Folder
-                        ? dragObject.originalDragInfo
-                                .buildProto(((Folder) dragObject.dragSource).mInfo)
-                        : dragObject.originalDragInfo.buildProto()
+                dragObject.originalDragInfo
         );
     }
 
@@ -1652,7 +1649,7 @@
             mStatsLogManager.log(
                     LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED,
                     d.logInstanceId,
-                    destInfo.buildProto(null));
+                    destInfo);
             FolderIcon fi = mLauncher.addFolder(target, container, screenId, targetCell[0],
                     targetCell[1]);
             destInfo.cellX = -1;
@@ -1693,7 +1690,7 @@
                 mStatsLogManager.log(
                         LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
                         d.logInstanceId,
-                        fi.mInfo.buildProto(null));
+                        fi.mInfo);
                 fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
 
                 // if the drag started here, we need to remove it from the workspace
@@ -1899,7 +1896,7 @@
             mStatsLogManager.log(
                     LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
                     d.logInstanceId,
-                    d.dragInfo.buildProto(null));
+                    d.dragInfo);
         }
 
         if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
@@ -2440,7 +2437,7 @@
                     mStatsLogManager.log(
                             LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
                             d.logInstanceId,
-                            d.dragInfo.buildProto(null));
+                            d.dragInfo);
                 }
             };
             boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
@@ -2532,7 +2529,7 @@
             mStatsLogManager.log(
                     LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
                     d.logInstanceId,
-                    d.dragInfo.buildProto(null));
+                    d.dragInfo);
         }
 
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 8260336..1dd81e8 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -14,7 +14,6 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
-import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -37,7 +36,6 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ScrimView;
 import com.android.systemui.plugins.AllAppsSearchPlugin;
 import com.android.systemui.plugins.PluginListener;
@@ -75,7 +73,6 @@
     private ScrimView mScrimView;
 
     private final Launcher mLauncher;
-    private final boolean mIsDarkTheme;
     private boolean mIsVerticalLayout;
 
     // Animation in this class is controlled by a single variable {@link mProgress}.
@@ -98,7 +95,6 @@
         mShiftRange = mLauncher.getDeviceProfile().heightPx;
         mProgress = 1f;
 
-        mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
         mIsVerticalLayout = mLauncher.getDeviceProfile().isVerticalBarLayout();
         mLauncher.addOnDeviceProfileChangeListener(this);
     }
@@ -137,16 +133,6 @@
         if (mPlugin != null) {
             mPlugin.setProgress(progress);
         }
-
-        // Use a light system UI (dark icons) if all apps is behind at least half of the
-        // status bar.
-        boolean forceChange = Math.min(shiftCurrent, mScrimView.getVisualTop())
-                <= mLauncher.getDeviceProfile().getInsets().top / 2f;
-        if (forceChange) {
-            mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, !mIsDarkTheme);
-        } else {
-            mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
-        }
     }
 
     public float getProgress() {
diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java
index 06d0f1c..6ea38ec 100644
--- a/src/com/android/launcher3/anim/FlingSpringAnim.java
+++ b/src/com/android/launcher3/anim/FlingSpringAnim.java
@@ -35,6 +35,7 @@
 
     private final FlingAnimation mFlingAnim;
     private SpringAnimation mSpringAnim;
+    private final boolean mSkipFlingAnim;
 
     private float mTargetPosition;
 
@@ -57,6 +58,10 @@
                 .setMaxValue(maxValue);
         mTargetPosition = targetPosition;
 
+        // We are already past the fling target, so skip it to avoid losing a frame of the spring.
+        mSkipFlingAnim = startPosition <= minValue && startVelocity < 0
+                || startPosition >= maxValue && startVelocity > 0;
+
         mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
             mSpringAnim = new SpringAnimation(object, property)
                     .setStartValue(value)
@@ -84,6 +89,9 @@
 
     public void start() {
         mFlingAnim.start();
+        if (mSkipFlingAnim) {
+            mFlingAnim.cancel();
+        }
     }
 
     public void end() {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index c7487cb..530010e 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1338,7 +1338,7 @@
             d.stateAnnouncer.completeAction(R.string.item_moved);
         }
         mStatsLogManager
-                .log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo.buildProto(mInfo));
+                .log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo);
     }
 
     // This is used so the item doesn't immediately appear in the folder when added. In one case
@@ -1443,7 +1443,7 @@
             if (hasFocus) {
                 startEditingFolderName();
             } else {
-                mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo.buildProto());
+                mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo);
                 logFolderLabelState();
                 mFolderName.dispatchBackKey();
             }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 153d6bc..2be5883 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -201,8 +201,7 @@
         icon.mActivity = activity;
         icon.mDotRenderer = grid.mDotRendererWorkSpace;
 
-        icon.setContentDescription(
-                group.getContext().getString(R.string.folder_name_format, folderInfo.title));
+        icon.setContentDescription(icon.getAccessiblityTitle(folderInfo.title));
 
         // Keep the notification dot up to date with the sum of all the content's dots.
         FolderDotInfo folderDotInfo = new FolderDotInfo();
@@ -450,7 +449,7 @@
         }
         mInfo.setTitle(nameInfos[0].getLabel());
         StatsLogManager.newInstance(getContext())
-                .log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo.buildProto());
+                .log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo);
         onTitleChanged(mInfo.title);
         mFolder.mFolderName.setText(mInfo.title);
         mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -665,6 +664,7 @@
         mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item));
         boolean isDotted = mDotInfo.hasDot();
         updateDotScale(wasDotted, isDotted);
+        setContentDescription(getAccessiblityTitle(mInfo.title));
         invalidate();
         requestLayout();
     }
@@ -675,13 +675,14 @@
         mDotInfo.subtractDotInfo(mActivity.getDotInfoForItem(item));
         boolean isDotted = mDotInfo.hasDot();
         updateDotScale(wasDotted, isDotted);
+        setContentDescription(getAccessiblityTitle(mInfo.title));
         invalidate();
         requestLayout();
     }
 
     public void onTitleChanged(CharSequence title) {
         mFolderName.setText(title);
-        setContentDescription(getContext().getString(R.string.folder_name_format, title));
+        setContentDescription(getAccessiblityTitle(title));
     }
 
     @Override
@@ -775,4 +776,17 @@
     public void getWorkspaceVisualDragBounds(Rect bounds) {
         getPreviewBounds(bounds);
     }
+
+    /**
+     * Returns a formatted accessibility title for folder
+     */
+    public String getAccessiblityTitle(CharSequence title) {
+        int size = mInfo.contents.size();
+        if (size < MAX_NUM_ITEMS_IN_PREVIEW) {
+            return getContext().getString(R.string.folder_name_format_exact, title, size);
+        } else {
+            return getContext().getString(R.string.folder_name_format_overflow, title,
+                    MAX_NUM_ITEMS_IN_PREVIEW);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index ce70a32..e95c062 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -21,8 +21,8 @@
 
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
-import com.android.launcher3.logger.LauncherAtom.ItemInfo;
 import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ResourceBasedOverride;
 
 /**
@@ -191,32 +191,37 @@
     }
 
     /**
-     * Logs a {@link EventEnum}.
+     * Logs an event.
+     *
+     * @param event an enum implementing EventEnum interface.
      */
     public void log(EventEnum event) {
     }
 
     /**
-     * Logs an event and accompanying {@link InstanceId}.
+     * Logs an event.
+     *
+     * @param event an enum implementing EventEnum interface.
+     * @param instanceId an identifier obtained from an InstanceIdSequence.
      */
     public void log(EventEnum event, InstanceId instanceId) {
     }
 
     /**
-     * Logs an event and accompanying {@link ItemInfo}.
+     * Logs an event.
+     *
+     * @param event an enum implementing EventEnum interface.
+     * @param itemInfo item typically containing app or task launch related information.
      */
     public void log(EventEnum event, @Nullable ItemInfo itemInfo) {
     }
 
     /**
-     * Logs an event and accompanying {@link com.android.launcher3.model.data.ItemInfo}.
-     */
-    public void log(EventEnum event,
-            com.android.launcher3.model.data.ItemInfo itemInfo) {
-    }
-
-    /**
-     * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
+     * Logs an event.
+     *
+     * @param event an enum implementing EventEnum interface.
+     * @param instanceId an identifier obtained from an InstanceIdSequence.
+     * @param itemInfo item typically containing app or task launch related information.
      */
     public void log(EventEnum event, InstanceId instanceId, @Nullable ItemInfo itemInfo) {
     }
@@ -225,7 +230,7 @@
      * Log an event with ranked-choice information along with package. Does nothing if event.getId()
      * <= 0.
      *
-     * @param rankingEvent an enum implementing UiEventEnum interface.
+     * @param rankingEvent an enum implementing EventEnum interface.
      * @param instanceId An identifier obtained from an InstanceIdSequence.
      * @param packageName the package name of the relevant app, if known (null otherwise).
      * @param position the position picked.
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index d52b7eb..66c3cbb 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -24,11 +24,13 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SEARCH_RESULTS;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SETTINGS;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
 
 import android.content.ComponentName;
@@ -49,6 +51,7 @@
 import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
 import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
 import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
+import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
 import com.android.launcher3.util.ContentWriter;
 
 import java.util.Optional;
@@ -298,6 +301,12 @@
                                 .setSpanX(spanX)
                                 .setSpanY(spanY));
                 break;
+            case ITEM_TYPE_TASK:
+                itemBuilder
+                        .setTask(LauncherAtom.Task.newBuilder()
+                                .setComponentName(getTargetComponent().flattenToShortString())
+                                .setIndex(screenId));
+                break;
             default:
                 break;
         }
@@ -378,6 +387,11 @@
                 return ContainerInfo.newBuilder()
                         .setSettingsContainer(SettingsContainer.getDefaultInstance())
                         .build();
+            case CONTAINER_TASKSWITCHER:
+                return ContainerInfo.newBuilder()
+                        .setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance())
+                        .build();
+
         }
         return ContainerInfo.getDefaultInstance();
     }
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index fa1bdfb..f1b63f2 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -108,7 +108,7 @@
             intent.send(null, 0, null, null, null, null, activityOptions);
             launcher.getUserEventDispatcher().logNotificationLaunch(view, intent);
             launcher.getStatsLogManager()
-                    .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP, mItemInfo.buildProto());
+                    .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP, mItemInfo);
         } catch (PendingIntent.CanceledException e) {
             e.printStackTrace();
         }
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 58251e8..8e60c27 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -78,7 +78,7 @@
     public void onClick(View view) {
         AbstractFloatingView.closeAllOpenViews(mTarget);
         mTarget.getStatsLogManager()
-                .log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP, mItemInfo.buildProto());
+                .log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP, mItemInfo);
 
         final String actionIdentity = mAction.getTitle() + ", "
                 + mItemInfo.getTargetComponent().getPackageName();
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index ea8caf5..59d24de 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -119,9 +119,7 @@
             widgetsBottomSheet.populateAndShow(mItemInfo);
             mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                     ControlType.WIDGETS_BUTTON, view);
-            // TODO(thiruram): Fix missing container info when item is inside folder.
-            mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP,
-                    mItemInfo.buildProto());
+            mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP, mItemInfo);
         }
     }
 
@@ -142,9 +140,8 @@
                     mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
             mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                     ControlType.APPINFO_TARGET, view);
-            // TODO(thiruram): Fix missing container info when item is inside folder.
             mTarget.getStatsLogManager()
-                    .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP, mItemInfo.buildProto());
+                    .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP, mItemInfo);
         }
     }
 
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 53cc157..275c024 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -30,7 +30,7 @@
 
     // Various UI states in increasing order of priority
     public static final int UI_STATE_BASE_WINDOW = 0;
-    public static final int UI_STATE_ALL_APPS = 1;
+    public static final int UI_STATE_SCRIM_VIEW = 1;
     public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
     public static final int UI_STATE_OVERVIEW = 3;
 
@@ -61,25 +61,38 @@
         // Apply the state flags in priority order
         int newFlags = oldFlags;
         for (int stateFlag : mStates) {
-            if (Utilities.ATLEAST_OREO) {
-                if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
-                    newFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-                } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
-                    newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
-                }
-            }
-
-            if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
-                newFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-            } else if ((stateFlag & FLAG_DARK_STATUS) != 0) {
-                newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
-            }
+            newFlags = getSysUiVisibilityFlags(stateFlag, newFlags);
         }
         if (newFlags != oldFlags) {
             mWindow.getDecorView().setSystemUiVisibility(newFlags);
         }
     }
 
+    /**
+     * Returns the sysui visibility for the base layer
+     */
+    public int getBaseSysuiVisibility() {
+        return getSysUiVisibilityFlags(
+                mStates[UI_STATE_BASE_WINDOW], mWindow.getDecorView().getSystemUiVisibility());
+    }
+
+    private int getSysUiVisibilityFlags(int stateFlag, int currentVisibility) {
+        if (Utilities.ATLEAST_OREO) {
+            if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
+                currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+            } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
+                currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+            }
+        }
+
+        if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
+            currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        } else if ((stateFlag & FLAG_DARK_STATUS) != 0) {
+            currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        }
+        return currentVisibility;
+    }
+
     @Override
     public String toString() {
         return "mStates=" + Arrays.toString(mStates);
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index a2c7d14..22faf97 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -27,6 +27,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -53,6 +54,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.core.graphics.ColorUtils;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
@@ -77,7 +79,6 @@
 
 import java.util.List;
 
-
 /**
  * Simple scrim which draws a flat color
  */
@@ -115,6 +116,7 @@
     private final WallpaperColorInfo mWallpaperColorInfo;
     private final AccessibilityManager mAM;
     protected final int mEndScrim;
+    protected final boolean mIsScrimDark;
 
     private final StateListener<LauncherState> mAccessibilityLauncherStateListener =
             new StateListener<LauncherState>() {
@@ -156,6 +158,7 @@
         mLauncher = Launcher.cast(Launcher.getLauncher(context));
         mWallpaperColorInfo = WallpaperColorInfo.INSTANCE.get(context);
         mEndScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+        mIsScrimDark = ColorUtils.calculateLuminance(mEndScrim) < 0.5f;
 
         mMaxScrimAlpha = 0.7f;
 
@@ -233,6 +236,7 @@
             mProgress = progress;
             stopDragHandleEducationAnim();
             updateColors();
+            updateSysUiColors();
             updateDragHandleAlpha();
             invalidate();
         }
@@ -245,6 +249,17 @@
                 mEndFlatColor, Math.round((1 - mProgress) * mEndFlatColorAlpha));
     }
 
+    protected void updateSysUiColors() {
+        // Use a light system UI (dark icons) if all apps is behind at least half of the
+        // status bar.
+        boolean forceChange = mProgress <= 0.1f;
+        if (forceChange) {
+            mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
+        } else {
+            mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
+        }
+    }
+
     protected void updateDragHandleAlpha() {
         if (mDragHandle != null) {
             mDragHandle.setAlpha(mDragHandleAlpha);