Merge "Add a feature flag to expand smartspace to three rows" into ub-launcher3-master
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 5491daa..6c88e55 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -144,7 +144,7 @@
                     LauncherActivityInterface.INSTANCE);
             tvs.setDp(mDeviceProfile);
 
-            int launcherRotation = DisplayController.INSTANCE.get(mContext).getInfo().rotation;
+            int launcherRotation = DisplayController.getDefaultDisplay(mContext).getInfo().rotation;
             if (mAppRotation < 0) {
                 mAppRotation = launcherRotation;
             }
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index df1833d..54a753c 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -802,33 +802,31 @@
     }
 
     private void addCujInstrumentation(Animator anim, int cuj, String transition) {
-        if (Trace.isEnabled()) {
-            anim.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    Trace.beginAsyncSection(transition, 0);
-                    InteractionJankMonitorWrapper.begin(cuj);
-                    super.onAnimationStart(animation);
-                }
+        anim.addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                Trace.beginAsyncSection(transition, 0);
+                InteractionJankMonitorWrapper.begin(cuj);
+                super.onAnimationStart(animation);
+            }
 
-                @Override
-                public void onAnimationCancel(Animator animation) {
-                    super.onAnimationCancel(animation);
-                    InteractionJankMonitorWrapper.cancel(cuj);
-                }
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                InteractionJankMonitorWrapper.cancel(cuj);
+            }
 
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    InteractionJankMonitorWrapper.end(cuj);
-                }
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                InteractionJankMonitorWrapper.end(cuj);
+            }
 
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    super.onAnimationEnd(animation);
-                    Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0);
-                }
-            });
-        }
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0);
+            }
+        });
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index cc109f6..23d62b3 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -618,13 +618,6 @@
 
         updateSysUiFlags(mCurrentShift.value);
         applyWindowTransform();
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (mRecentsAnimationTargets != null) {
-                LiveTileOverlay.INSTANCE.update(
-                        mTaskViewSimulator.getCurrentRect(),
-                        mTaskViewSimulator.getCurrentCornerRadius());
-            }
-        }
 
         updateLauncherTransitionProgress();
     }
@@ -800,6 +793,8 @@
     }
 
     private void onSettledOnEndTarget() {
+        // Fast-finish the attaching animation if it's still running.
+        maybeUpdateRecentsAttachedState(false);
         final GestureEndTarget endTarget = mGestureState.getEndTarget();
         switch (endTarget) {
             case HOME:
@@ -1616,6 +1611,11 @@
             }
             mTaskViewSimulator.apply(mTransformParams);
         }
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mRecentsAnimationTargets != null) {
+            LiveTileOverlay.INSTANCE.update(
+                    mTaskViewSimulator.getCurrentRect(),
+                    mTaskViewSimulator.getCurrentCornerRadius());
+        }
         ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
     }
 
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 5520ef7..46c7768 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -41,6 +41,7 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
@@ -129,6 +130,21 @@
         return taskView;
     }
 
+    public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
+            PendingAnimation out) {
+        boolean isRunningTask = v.isRunningTask();
+        TransformParams params = null;
+        TaskViewSimulator tsv = null;
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
+            params = v.getRecentsView().getLiveTileParams();
+            tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
+        }
+        createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets,
+                depthController, out, params, tsv);
+    }
+
     /**
      * Creates an animation that controls the window of the opening targets for the recents launch
      * animation.
@@ -136,19 +152,25 @@
     public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
             RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
-            PendingAnimation out) {
+            PendingAnimation out, @Nullable TransformParams params,
+            @Nullable TaskViewSimulator tsv) {
         boolean isQuickSwitch = v.isEndQuickswitchCuj();
         v.setEndQuickswitchCuj(false);
 
-        SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+        boolean inLiveTileMode =
+                ENABLE_QUICKSTEP_LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
         final RemoteAnimationTargets targets =
                 new RemoteAnimationTargets(appTargets, wallpaperTargets,
-                        ENABLE_QUICKSTEP_LIVE_TILE.get() ? MODE_CLOSING : MODE_OPENING);
-        targets.addReleaseCheck(applier);
+                        inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
 
-        TransformParams params = new TransformParams()
+        if (params == null) {
+            SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+            targets.addReleaseCheck(applier);
+
+            params = new TransformParams()
                     .setSyncTransactionApplier(applier)
                     .setTargetSet(targets);
+        }
 
         final RecentsView recentsView = v.getRecentsView();
         int taskIndex = recentsView.indexOfChild(v);
@@ -162,8 +184,9 @@
         int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
 
         TaskViewSimulator topMostSimulator = null;
-        if (targets.apps.length > 0) {
-            TaskViewSimulator tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
+
+        if (tsv == null && targets.apps.length > 0) {
+            tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
             tsv.setDp(dp);
             tsv.setLayoutRotation(displayRotation, displayRotation);
             tsv.setPreview(targets.apps[targets.apps.length - 1]);
@@ -171,19 +194,24 @@
             tsv.recentsViewScale.value = 1;
             tsv.setScroll(startScroll);
 
+            // Fade in the task during the initial 20% of the animation
+            out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
+                    clampToProgress(LINEAR, 0, 0.2f));
+        }
+
+        if (tsv != null) {
             out.setFloat(tsv.fullScreenProgress,
                     AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
             out.setFloat(tsv.recentsViewScale,
                     AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
             out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
 
-            out.addOnFrameCallback(() -> tsv.apply(params));
+            TaskViewSimulator finalTsv = tsv;
+            TransformParams finalParams = params;
+            out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
             topMostSimulator = tsv;
         }
 
-        // Fade in the task during the initial 20% of the animation
-        out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, clampToProgress(LINEAR, 0, 0.2f));
-
         if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
             out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
 
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
index 6862f07..5c81e5f 100644
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
@@ -104,7 +104,13 @@
                         hideFeedback();
                         hideHandCoachingAnimation();
                         showRippleEffect(
-                                () -> mTutorialFragment.changeController(ASSISTANT_COMPLETE));
+                                () -> {
+                                    if (mTutorialFragment.isTutorialComplete()) {
+                                        mTutorialFragment.changeController(ASSISTANT_COMPLETE);
+                                    } else {
+                                        mTutorialFragment.continueTutorial();
+                                    }
+                                });
                         break;
                     case ASSISTANT_NOT_STARTED_BAD_ANGLE:
                         showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 921e568..161e015 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -130,7 +130,13 @@
                 hideFeedback();
                 hideHandCoachingAnimation();
                 showRippleEffect(
-                        () -> mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE));
+                        () -> {
+                            if (mTutorialFragment.isTutorialComplete()) {
+                                mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE);
+                            } else {
+                                mTutorialFragment.continueTutorial();
+                            }
+                        });
                 break;
             case BACK_CANCELLED_FROM_LEFT:
                 showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index f8d9d8d..8b6777b 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -15,8 +15,6 @@
  */
 package com.android.quickstep.interaction;
 
-import static com.android.quickstep.interaction.TutorialFragment.KEY_TUTORIAL_TYPE;
-
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -25,11 +23,14 @@
 import android.view.View;
 import android.view.Window;
 
+import androidx.annotation.NonNull;
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
+import java.util.ArrayDeque;
+import java.util.Deque;
 import java.util.List;
 
 /** Shows the gesture interactive sandbox in full screen mode. */
@@ -37,6 +38,9 @@
 
     private static final String LOG_TAG = "GestureSandboxActivity";
 
+    private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
+
+    private Deque<TutorialType> mTutorialSteps;
     private TutorialFragment mFragment;
 
     @Override
@@ -45,7 +49,9 @@
         requestWindowFeature(Window.FEATURE_NO_TITLE);
         setContentView(R.layout.gesture_tutorial_activity);
 
-        mFragment = TutorialFragment.newInstance(getTutorialType(getIntent().getExtras()));
+        Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
+        mTutorialSteps = getTutorialSteps(args);
+        mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
         getSupportFragmentManager().beginTransaction()
                 .add(R.id.gesture_tutorial_fragment_container, mFragment)
                 .commit();
@@ -72,17 +78,65 @@
         }
     }
 
-    private TutorialType getTutorialType(Bundle extras) {
-        TutorialType defaultType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
+    @Override
+    protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+        savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
+        super.onSaveInstanceState(savedInstanceState);
+    }
 
-        if (extras == null || !extras.containsKey(KEY_TUTORIAL_TYPE)) {
-            return defaultType;
+    /** Returns true iff there aren't anymore tutorial types to display to the user. */
+    public boolean isTutorialComplete() {
+        return mTutorialSteps.isEmpty();
+    }
+
+    /**
+     * Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
+     *
+     * If there is no following step, the tutorial is closed.
+     */
+    public void continueTutorial() {
+        if (isTutorialComplete()) {
+            mFragment.closeTutorial();
+            return;
         }
-        try {
-            return TutorialType.valueOf(extras.getString(KEY_TUTORIAL_TYPE, ""));
-        } catch (IllegalArgumentException e) {
-            return defaultType;
+        mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
+        getSupportFragmentManager().beginTransaction()
+            .replace(R.id.gesture_tutorial_fragment_container, mFragment)
+            .runOnCommit(() -> mFragment.onAttachedToWindow())
+            .commit();
+    }
+
+    private String[] getTutorialStepNames() {
+        String[] tutorialStepNames = new String[mTutorialSteps.size()];
+
+        int i = 0;
+        for (TutorialType tutorialStep : mTutorialSteps) {
+            tutorialStepNames[i++] = tutorialStep.name();
         }
+
+        return tutorialStepNames;
+    }
+
+    private Deque<TutorialType> getTutorialSteps(Bundle extras) {
+        Deque<TutorialType> defaultSteps = new ArrayDeque<>();
+        defaultSteps.push(TutorialType.RIGHT_EDGE_BACK_NAVIGATION);
+
+        if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
+            return defaultSteps;
+        }
+
+        String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS);
+
+        if (tutorialStepNames == null) {
+            return defaultSteps;
+        }
+
+        Deque<TutorialType> tutorialSteps = new ArrayDeque<>();
+        for (String tutorialStepName : tutorialStepNames) {
+            tutorialSteps.addLast(TutorialType.valueOf(tutorialStepName));
+        }
+
+        return tutorialSteps;
     }
 
     private void hideSystemUI() {
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 0edabd4..95352d1 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -94,8 +94,13 @@
             case HOME_NAVIGATION:
                 switch (result) {
                     case HOME_GESTURE_COMPLETED: {
-                        animateFakeTaskViewHome(finalVelocity, () ->
-                                mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE));
+                        animateFakeTaskViewHome(finalVelocity, () -> {
+                            if (mTutorialFragment.isTutorialComplete()) {
+                                mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE);
+                            } else {
+                                mTutorialFragment.continueTutorial();
+                            }
+                        });
                         break;
                     }
                     case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index c636eba..45cbd0b 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -104,8 +104,13 @@
                         showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
                         break;
                     case OVERVIEW_GESTURE_COMPLETED:
-                        fadeOutFakeTaskView(true, () ->
-                                mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE));
+                        fadeOutFakeTaskView(true, () -> {
+                            if (mTutorialFragment.isTutorialComplete()) {
+                                mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE);
+                            } else {
+                                mTutorialFragment.continueTutorial();
+                            }
+                        });
                         break;
                     case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
                     case HOME_OR_OVERVIEW_CANCELLED:
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index f297d5a..608fe72 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -54,6 +54,7 @@
             fragment = new BackGestureTutorialFragment();
             tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
         }
+
         Bundle args = new Bundle();
         args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
         fragment.setArguments(args);
@@ -197,6 +198,20 @@
         return mHandCoachingAnimation;
     }
 
+    void continueTutorial() {
+        if (!(getContext() instanceof GestureSandboxActivity)) {
+            closeTutorial();
+            return;
+        }
+        GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
+
+        if (gestureSandboxActivity == null) {
+            closeTutorial();
+            return;
+        }
+        gestureSandboxActivity.continueTutorial();
+    }
+
     void closeTutorial() {
         FragmentActivity activity = getActivity();
         if (activity != null) {
@@ -207,4 +222,13 @@
     void startSystemNavigationSetting() {
         startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
     }
+
+    boolean isTutorialComplete() {
+        if (!(getContext() instanceof GestureSandboxActivity)) {
+            return true;
+        }
+        GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
+
+        return gestureSandboxActivity == null || gestureSandboxActivity.isTutorialComplete();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
index c6c2d7e..f6eb0e2 100644
--- a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -65,6 +65,10 @@
         invalidateSelf();
     }
 
+    public void update(float left, float top, float right, float bottom) {
+        mCurrentRect.set(left, top, right, bottom);
+    }
+
     public void setIcon(Drawable icon) {
         mIcon = icon;
     }
@@ -94,18 +98,16 @@
 
     @Override
     public void draw(Canvas canvas) {
-        if (mCurrentRect != null) {
-            canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
-            if (mIcon != null && mIconAnimationProgress > 0f) {
-                canvas.save();
-                float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
-                        1f).getInterpolation(mIconAnimationProgress);
-                canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
-                        mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
-                canvas.scale(scale, scale);
-                mIcon.draw(canvas);
-                canvas.restore();
-            }
+        canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
+        if (mIcon != null && mIconAnimationProgress > 0f) {
+            canvas.save();
+            float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
+                    1f).getInterpolation(mIconAnimationProgress);
+            canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
+                    mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
+            canvas.scale(scale, scale);
+            mIcon.draw(canvas);
+            canvas.restore();
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2158e03..826555c 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -230,6 +230,7 @@
                     view.setScaleX(scale);
                     view.setScaleY(scale);
                     view.mLastComputedTaskPushOutDistance = null;
+                    view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
                     view.updatePageOffsets();
                     view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
                 }
@@ -876,6 +877,10 @@
             mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
             mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
             mLiveTileTaskViewSimulator.setOffsetY(0);
+
+            // Reset the live tile rect
+            DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+            LiveTileOverlay.INSTANCE.update(0, 0, deviceProfile.widthPx, deviceProfile.heightPx);
         }
         if (mRunningTaskTileHidden) {
             setRunningTaskHidden(mRunningTaskTileHidden);
@@ -1986,6 +1991,7 @@
             TaskView task = getTaskViewAt(i);
             mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
         }
+        mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
     }
 
     /**
@@ -2264,6 +2270,10 @@
         return mLiveTileTaskViewSimulator;
     }
 
+    public TransformParams getLiveTileParams() {
+        return mLiveTileParams;
+    }
+
     // TODO: To be removed in a follow up CL
     public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
             RecentsAnimationTargets recentsAnimationTargets) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 54a793c..5154018 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -39,6 +39,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
@@ -65,7 +66,6 @@
 import android.widget.FrameLayout;
 import android.widget.Toast;
 
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
@@ -75,6 +75,7 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
@@ -82,10 +83,12 @@
 import com.android.launcher3.util.TransformingTouchDelegate;
 import com.android.launcher3.util.ViewPool.Reusable;
 import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.TaskIconCache;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskThumbnailCache;
 import com.android.quickstep.TaskUtils;
+import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.util.CancellableTask;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.TaskCornerRadius;
@@ -175,7 +178,7 @@
     private float mCurveScale;
     private float mFullscreenProgress;
     private final FullscreenDrawParams mCurrentFullscreenParams;
-    private final BaseDraggingActivity mActivity;
+    private final StatefulActivity mActivity;
 
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
@@ -212,18 +215,31 @@
 
     public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mActivity = BaseDraggingActivity.fromContext(context);
+        mActivity = StatefulActivity.fromContext(context);
         setOnClickListener((view) -> {
             if (getTask() == null) {
                 return;
             }
-            if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-                if (isRunningTask()) {
-                    // TODO: Replace this animation with createRecentsWindowAnimator
-                    createLaunchAnimationForRunningTask().start();
-                } else {
-                    launchTask(true /* animate */);
-                }
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
+                RecentsView recentsView = getRecentsView();
+                RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
+                recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
+
+                AnimatorSet anim = new AnimatorSet();
+                TaskViewUtils.composeRecentsLaunchAnimator(
+                        anim, this, targets.apps,
+                        targets.wallpapers, true /* launcherClosing */,
+                        mActivity.getStateManager(), recentsView,
+                        recentsView.getDepthController());
+                anim.addListener(new AnimatorListenerAdapter() {
+
+                    @Override
+                    public void onAnimationEnd(Animator animator) {
+                        recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
+                        recentsView.finishRecentsAnimation(false, null);
+                    }
+                });
+                anim.start();
             } else {
                 launchTask(true /* animate */);
             }
diff --git a/res/layout/switch_preference_with_settings.xml b/res/layout/switch_preference_with_settings.xml
index d319561..cd51833 100644
--- a/res/layout/switch_preference_with_settings.xml
+++ b/res/layout/switch_preference_with_settings.xml
@@ -26,7 +26,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:src="@drawable/ic_setting"
-        android:tint="@android:color/black"
+        android:forceDarkAllowed="true"
         android:padding="12dp"
         android:background="?android:attr/selectableItemBackgroundBorderless" />
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 370bd6f..ff53b5f 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -201,7 +201,7 @@
                 DisplayController.getDefaultDisplay(context).getInfo(),
                 getPredefinedDeviceProfiles(context, gridName));
 
-        Info myInfo = new Info(context, display);
+        Info myInfo = new Info(display);
         DisplayOption myDisplayOption = invDistWeightedInterpolate(
                 myInfo, getPredefinedDeviceProfiles(context, gridName));
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 254f9cd..777ea3c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2085,40 +2085,40 @@
         mLastReorderY = -1;
     }
 
-   /*
-    *
-    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
-    * coordinate space. The argument xy is modified with the return result.
-    */
-   private void mapPointFromSelfToChild(View v, float[] xy) {
-       xy[0] = xy[0] - v.getLeft();
-       xy[1] = xy[1] - v.getTop();
-   }
+    /*
+     *
+     * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
+     * coordinate space. The argument xy is modified with the return result.
+     */
+    private void mapPointFromSelfToChild(View v, float[] xy) {
+        xy[0] = xy[0] - v.getLeft();
+        xy[1] = xy[1] - v.getTop();
+    }
 
-   boolean isPointInSelfOverHotseat(int x, int y) {
-       mTempFXY[0] = x;
-       mTempFXY[1] = y;
-       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
-       View hotseat = mLauncher.getHotseat();
-       return mTempFXY[0] >= hotseat.getLeft() &&
-               mTempFXY[0] <= hotseat.getRight() &&
-               mTempFXY[1] >= hotseat.getTop() &&
-               mTempFXY[1] <= hotseat.getBottom();
-   }
+    boolean isPointInSelfOverHotseat(int x, int y) {
+        mTempFXY[0] = x;
+        mTempFXY[1] = y;
+        mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
+        View hotseat = mLauncher.getHotseat();
+        return mTempFXY[0] >= hotseat.getLeft()
+                && mTempFXY[0] <= hotseat.getRight()
+                && mTempFXY[1] >= hotseat.getTop()
+                && mTempFXY[1] <= hotseat.getBottom();
+    }
 
     /**
      * Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
      * @param layout either hotseat of a page in workspace
      * @param xy the point location in workspace co-ordinate space
      */
-   private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
-       if (mLauncher.isHotseatLayout(layout)) {
-           mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
-           mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
-       } else {
-           mapPointFromSelfToChild(layout, xy);
-       }
-   }
+    private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
+        if (mLauncher.isHotseatLayout(layout)) {
+            mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
+            mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
+        } else {
+            mapPointFromSelfToChild(layout, xy);
+        }
+    }
 
     private boolean isDragWidget(DragObject d) {
         return (d.dragInfo instanceof LauncherAppWidgetInfo ||
@@ -2400,9 +2400,9 @@
             spanY = mDragInfo.spanY;
         }
 
-        final int container = mLauncher.isHotseatLayout(cellLayout) ?
-                LauncherSettings.Favorites.CONTAINER_HOTSEAT :
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP;
+        final int container = mLauncher.isHotseatLayout(cellLayout)
+                ? LauncherSettings.Favorites.CONTAINER_HOTSEAT
+                : LauncherSettings.Favorites.CONTAINER_DESKTOP;
         final int screenId = getIdForScreen(cellLayout);
         if (!mLauncher.isHotseatLayout(cellLayout)
                 && screenId != getScreenIdForPageIndex(mCurrentPage)
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 72b6d94..1fa43d0 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -24,6 +24,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.SparseIntArray;
 import android.view.MotionEvent;
 import android.view.View;
@@ -45,6 +46,8 @@
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
 public class AllAppsRecyclerView extends BaseRecyclerView {
+    private static final String TAG = "AllAppsContainerView";
+    private static final boolean DEBUG = true;
 
     private AlphabeticalAppsList mApps;
     private final int mNumAppsPerRow;
@@ -131,7 +134,9 @@
         if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
             mEmptySearchBackground.draw(c);
         }
-
+        if (DEBUG) {
+            Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
+        }
         super.onDraw(c);
     }
 
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 8c059d5..2450787 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -178,16 +178,46 @@
     /**
      * Sets results list for search
      */
-    public boolean setSearchResults(ArrayList<AdapterItem> f) {
-        if (f == null || mSearchResults != f) {
-            boolean same = mSearchResults != null && mSearchResults.equals(f);
-            mSearchResults = f;
+    public boolean setSearchResults(ArrayList<AdapterItem> results) {
+        if (results == null || mSearchResults != results) {
+            boolean same = mSearchResults != null && mSearchResults.equals(results);
+            mSearchResults = results;
             onAppsUpdated();
             return !same;
         }
         return false;
     }
 
+    public boolean appendSearchResults(ArrayList<AdapterItem> results) {
+        if (mSearchResults != null && results != null && results.size() > 0) {
+            updateSearchAdapterItems(results, mSearchResults.size());
+            refreshRecyclerView();
+            return true;
+        }
+        return false;
+    }
+
+    void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
+        SearchSectionInfo lastSection = null;
+        for (int i = 0; i < list.size(); i++) {
+            AdapterItem adapterItem = list.get(i);
+            adapterItem.position = offset + i;
+            mAdapterItems.add(adapterItem);
+            if (adapterItem.searchSectionInfo != lastSection) {
+                if (adapterItem.searchSectionInfo != null) {
+                    adapterItem.searchSectionInfo.setPosStart(adapterItem.position);
+                }
+                if (lastSection != null) {
+                    lastSection.setPosEnd(adapterItem.position - 1);
+                }
+                lastSection = adapterItem.searchSectionInfo;
+            }
+            if (adapterItem.isCountedForAccessibility()) {
+                mAccessibilityResultsCount++;
+            }
+        }
+    }
+
     /**
      * Updates internals when the set of apps are updated.
      */
@@ -294,28 +324,7 @@
             }
             appSection.setPosEnd(mApps.isEmpty() ? appSection.getPosStart() : position - 1);
         } else {
-            List<AppInfo> appInfos = new ArrayList<>();
-            SearchSectionInfo lastSection = null;
-            for (int i = 0; i < mSearchResults.size(); i++) {
-                AdapterItem adapterItem = mSearchResults.get(i);
-                adapterItem.position = i;
-                mAdapterItems.add(adapterItem);
-                if (adapterItem.searchSectionInfo != lastSection) {
-                    if (adapterItem.searchSectionInfo != null) {
-                        adapterItem.searchSectionInfo.setPosStart(i);
-                    }
-                    if (lastSection != null) {
-                        lastSection.setPosEnd(i - 1);
-                    }
-                    lastSection = adapterItem.searchSectionInfo;
-                }
-                if (AllAppsGridAdapter.isIconViewType(adapterItem.viewType)) {
-                    appInfos.add(adapterItem.appInfo);
-                }
-                if (adapterItem.isCountedForAccessibility()) {
-                    mAccessibilityResultsCount++;
-                }
-            }
+            updateSearchAdapterItems(mSearchResults, 0);
             if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
                 // Append the search market item
                 if (hasNoFilteredResults()) {
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 9e6ca34..bc5a5f2 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -30,7 +30,7 @@
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.systemui.plugins.AllAppsSearchPlugin;
@@ -194,9 +194,16 @@
         /**
          * Called when the search from primary source is complete.
          *
-         * @param items sorted list of search result adapter items.
+         * @param items sorted list of search result adapter items
          */
-        void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items);
+        void onSearchResult(String query, ArrayList<AdapterItem> items);
+
+        /**
+         * Called when the search from secondary source is complete.
+         *
+         * @param items sorted list of search result adapter items
+         */
+        void onAppendSearchResult(String query, ArrayList<AdapterItem> items);
 
         /**
          * Called when the search results should be cleared.
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 7518521..000ccbb 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -42,7 +42,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AlphabeticalAppsList;
 import com.android.launcher3.allapps.SearchUiManager;
@@ -173,7 +173,7 @@
     }
 
     @Override
-    public void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items) {
+    public void onSearchResult(String query, ArrayList<AdapterItem> items) {
         if (items != null) {
             mApps.setSearchResults(items);
             notifyResultChanged();
@@ -182,6 +182,14 @@
     }
 
     @Override
+    public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
+        if (items != null) {
+            mApps.appendSearchResults(items);
+            notifyResultChanged();
+        }
+    }
+
+    @Override
     public void clearSearchResult() {
         if (mApps.setSearchResults(null)) {
             notifyResultChanged();
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
index dc9c155..84688e1 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.allapps.search;
 
 import android.content.Context;
+import android.os.CancellationSignal;
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
@@ -47,11 +48,12 @@
     }
 
     @Override
-    public void performSearch(String query, Consumer<ArrayList<AdapterItem>> callback) {
+    public void query(String input, Consumer<ArrayList<AdapterItem>> callback,
+            CancellationSignal cancellationSignal) {
         mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
             @Override
             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-                List<AppInfo> matchingResults = getTitleMatchResult(apps.data, query);
+                List<AppInfo> matchingResults = getTitleMatchResult(apps.data, input);
                 callback.accept(getAdapterItems(matchingResults));
             }
         });
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 5ed7de5..3bddace 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -46,8 +46,10 @@
     @Override
     public void doSearch(final String query,
             final AllAppsSearchBarController.Callbacks callback) {
-        mAppsSearchPipeline.performSearch(query,
-                results -> mResultHandler.post(() -> callback.onSearchResult(query, results)));
+        mAppsSearchPipeline.query(query,
+                results -> mResultHandler.post(
+                        () -> callback.onSearchResult(query, results)),
+                null);
     }
 
     public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
diff --git a/src/com/android/launcher3/allapps/search/SearchPipeline.java b/src/com/android/launcher3/allapps/search/SearchPipeline.java
index 545f0e3..3516a41 100644
--- a/src/com/android/launcher3/allapps/search/SearchPipeline.java
+++ b/src/com/android/launcher3/allapps/search/SearchPipeline.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.allapps.search;
 
+import android.os.CancellationSignal;
+
 import com.android.launcher3.allapps.AllAppsGridAdapter;
 
 import java.util.ArrayList;
@@ -23,10 +25,13 @@
 /**
  * An interface for handling search within pipeline
  */
+// Remove when System Service API is added.
 public interface SearchPipeline {
 
     /**
      * Perform query
      */
-    void performSearch(String query, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> cb);
+    void query(String input,
+            Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
+            CancellationSignal cancellationSignal);
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 7e27dad..8e6c2a7 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -182,7 +182,7 @@
             "Uses a separate recents activity instead of using the integrated recents+Launcher UI");
 
     public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
-            "ENABLE_MINIMAL_DEVICE", true,
+            "ENABLE_MINIMAL_DEVICE", false,
             "Allow user to toggle minimal device mode in launcher.");
 
     public static final BooleanFlag EXPANDED_SMARTSPACE = new DeviceFlag(
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index f4b059d..47d214d 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -275,7 +275,8 @@
         launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
         launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
             startActivity(launchSandboxIntent.putExtra(
-                    "tutorial_type", "RIGHT_EDGE_BACK_NAVIGATION"));
+                    "tutorial_steps",
+                    new String[] {"RIGHT_EDGE_BACK_NAVIGATION"}));
             return true;
         });
         sandboxCategory.addPreference(launchBackTutorialPreference);
@@ -284,7 +285,9 @@
         launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
         launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
         launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
-            startActivity(launchSandboxIntent.putExtra("tutorial_type", "HOME_NAVIGATION"));
+            startActivity(launchSandboxIntent.putExtra(
+                    "tutorial_steps",
+                    new String[] {"HOME_NAVIGATION"}));
             return true;
         });
         sandboxCategory.addPreference(launchHomeTutorialPreference);
@@ -293,7 +296,9 @@
         launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
         launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
         launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
-            startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION"));
+            startActivity(launchSandboxIntent.putExtra(
+                    "tutorial_steps",
+                    new String[] {"OVERVIEW_NAVIGATION"}));
             return true;
         });
         sandboxCategory.addPreference(launchOverviewTutorialPreference);
@@ -302,7 +307,9 @@
         launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
         launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
         launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
-            startActivity(launchSandboxIntent.putExtra("tutorial_type", "ASSISTANT"));
+            startActivity(launchSandboxIntent.putExtra(
+                    "tutorial_steps",
+                    new String[] {"ASSISTANT"}));
             return true;
         });
         sandboxCategory.addPreference(launchAssistantTutorialPreference);
@@ -311,7 +318,9 @@
         launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
         launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
         launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
-            startActivity(launchSandboxIntent.putExtra("tutorial_type", "SANDBOX_MODE"));
+            startActivity(launchSandboxIntent.putExtra(
+                    "tutorial_steps",
+                    new String[] {"SANDBOX_MODE"}));
             return true;
         });
         sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 355c949..3ab736a 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -31,6 +31,8 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.Utilities;
+
 import java.util.ArrayList;
 
 /**
@@ -157,13 +159,13 @@
         private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
         private DisplayController.Info mInfo;
 
-        private DisplayHolder(Context displayContext) {
+        private DisplayHolder(Context displayContext, Display display) {
             mDisplayContext = displayContext;
             // Note that the Display object must be obtained from DisplayManager which is
             // associated to the display context, so the Display is isolated from Activity and
             // Application to provide the actual state of device that excludes the additional
             // adjustment and override.
-            mInfo = new DisplayController.Info(mDisplayContext);
+            mInfo = new DisplayController.Info(display);
             mId = mInfo.id;
         }
 
@@ -180,22 +182,31 @@
         }
 
         protected void handleOnChange() {
+            Display display = Utilities.ATLEAST_R
+                    ? mDisplayContext.getDisplay()
+                    : mDisplayContext
+                        .getSystemService(DisplayManager.class)
+                        .getDisplay(mId);
+            if (display == null) {
+                return;
+            }
+
             Info oldInfo = mInfo;
-            Info info = new Info(mDisplayContext);
+            Info newInfo = new Info(display);
 
             int change = 0;
-            if (info.hasDifferentSize(oldInfo)) {
+            if (newInfo.hasDifferentSize(oldInfo)) {
                 change |= CHANGE_SIZE;
             }
-            if (oldInfo.rotation != info.rotation) {
+            if (newInfo.rotation != oldInfo.rotation) {
                 change |= CHANGE_ROTATION;
             }
-            if (info.singleFrameMs != oldInfo.singleFrameMs) {
+            if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
                 change |= CHANGE_FRAME_DELAY;
             }
 
             if (change != 0) {
-                mInfo = info;
+                mInfo = newInfo;
                 final int flags = change;
                 MAIN_EXECUTOR.execute(() -> notifyChange(flags));
             }
@@ -216,7 +227,7 @@
             // Use application context to create display context so that it can have its own
             // Resources.
             Context displayContext = context.getApplicationContext().createDisplayContext(display);
-            return new DisplayHolder(displayContext);
+            return new DisplayHolder(displayContext, display);
         }
     }
 
@@ -244,12 +255,7 @@
             this.metrics = metrics;
         }
 
-        private Info(Context context) {
-            this(context, context.getSystemService(DisplayManager.class)
-                    .getDisplay(DEFAULT_DISPLAY));
-        }
-
-        public Info(Context context, Display display) {
+        public Info(Display display) {
             id = display.getDisplayId();
             rotation = display.getRotation();
 
@@ -262,7 +268,8 @@
             display.getRealSize(realSize);
             display.getCurrentSizeRange(smallestSize, largestSize);
 
-            metrics = context.getResources().getDisplayMetrics();
+            metrics = new DisplayMetrics();
+            display.getMetrics(metrics);
         }
 
         private boolean hasDifferentSize(Info info) {
diff --git a/src/com/android/launcher3/views/SearchResultIcon.java b/src/com/android/launcher3/views/SearchResultIcon.java
index 7d9f534..ea06d5b 100644
--- a/src/com/android/launcher3/views/SearchResultIcon.java
+++ b/src/com/android/launcher3/views/SearchResultIcon.java
@@ -81,6 +81,7 @@
 
     @Override
     public void handleSelection(int eventType) {
+        mLauncher.getItemOnClickListener().onClick(this);
         SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
                 new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
     }
@@ -88,12 +89,12 @@
     @Override
     public void onClick(View view) {
         handleSelection(SearchTargetEvent.SELECT);
-        mLauncher.getItemOnClickListener().onClick(view);
     }
 
     @Override
     public boolean onLongClick(View view) {
-        handleSelection(SearchTargetEvent.LONG_PRESS);
+        SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
+                new SearchTargetEvent.Builder(mSearchTarget, SearchTargetEvent.LONG_PRESS).build());
         return ItemLongClickListener.INSTANCE_ALL_APPS.onLongClick(view);
     }
 }
diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java
index 5fb36b9..fe904ff 100644
--- a/src/com/android/launcher3/views/SearchResultIconRow.java
+++ b/src/com/android/launcher3/views/SearchResultIconRow.java
@@ -121,7 +121,7 @@
             prepareUsingRemoteAction(searchTarget.getRemoteAction(),
                     searchTarget.getExtras().getString(REMOTE_ACTION_TOKEN),
                     searchTarget.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START),
-                    type.equals(TARGET_TYPE_SUGGEST));
+                    type.equals(TARGET_TYPE_REMOTE_ACTION));
 
         } else if (type.equals(TARGET_TYPE_SHORTCUT)) {
             prepareUsingShortcutInfo(searchTarget.getShortcutInfos().get(0));
diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java
index 6d85955..86ed436 100644
--- a/src/com/android/launcher3/views/SearchResultPlayItem.java
+++ b/src/com/android/launcher3/views/SearchResultPlayItem.java
@@ -104,7 +104,6 @@
         iconParams.height = mDeviceProfile.allAppsIconSizePx;
         iconParams.width = mDeviceProfile.allAppsIconSizePx;
         setOnClickListener(view -> handleSelection(SearchTargetEvent.SELECT));
-
     }
 
 
diff --git a/src/com/android/launcher3/views/ThumbnailSearchResultView.java b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
index d11b1ef..e929d7f 100644
--- a/src/com/android/launcher3/views/ThumbnailSearchResultView.java
+++ b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
@@ -90,6 +90,7 @@
             bitmap = Bitmap.createBitmap(bitmap, 0,
                     bitmap.getHeight() / 2 - bitmap.getWidth() / 2,
                     bitmap.getWidth(), bitmap.getWidth());
+            setTag(itemInfo);
         } else {
             bitmap = (Bitmap) target.getExtras().getParcelable("bitmap");
             WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
index aa3ab8f..5cc238d 100644
--- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
+++ b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
@@ -17,6 +17,8 @@
 package com.android.systemui.plugins;
 
 import android.app.Activity;
+import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.view.View;
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
@@ -32,7 +34,7 @@
 @ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
 public interface AllAppsSearchPlugin extends Plugin {
     String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
-    int VERSION = 7;
+    int VERSION = 8;
 
     void setup(Activity activity, View view);
 
@@ -49,10 +51,21 @@
     void onWindowVisibilityChanged(int visibility);
 
     /**
-     * Send signal when user starts typing, perform search, when search ends
+     * Send signal when user starts typing, perform search, notify search target
+     * event when search ends.
      */
     void startedSearchSession();
-    void performSearch(String query, Consumer<List<SearchTarget>> results);
+
+    /**
+     * Main function that triggers search.
+     *
+     * @param input string that has been typed by a user
+     * @param inputArgs extra info that may be relevant for the input query
+     * @param results contains the result that will be rendered in all apps search surface
+     * @param cancellationSignal {@link CancellationSignal} can be used to share status of current
+     */
+    void query(String input, Bundle inputArgs, Consumer<List<SearchTarget>> results,
+            CancellationSignal cancellationSignal);
 
     /**
      * Send over search target interaction events to Plugin