Merge "Moving animation config so a separate class" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index e1ff4f4..aaf7619 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -48,7 +48,7 @@
  *
  * @param <T> activity that contains the overview
  */
-final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
+final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> extends
         RemoteAnimationProvider {
 
     private static final long RECENTS_LAUNCH_DURATION = 250;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index 71580ca..4abb86a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -213,7 +213,7 @@
     @Override
     public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
         // TODO: Remove this once b/77875376 is fixed
-        return target.sourceContainerBounds;
+        return target.screenSpaceBounds;
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index fc50660..c6b719a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -187,7 +188,15 @@
             // Otherwise, start overview.
             mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
             mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
-                    this::createWindowAnimation, mContext, MAIN_EXECUTOR.getHandler(),
+                    new RemoteAnimationProvider() {
+                        @Override
+                        public AnimatorSet createWindowAnimation(
+                                RemoteAnimationTargetCompat[] appTargets,
+                                RemoteAnimationTargetCompat[] wallpaperTargets) {
+                            return RecentsActivityCommand.this.createWindowAnimation(appTargets,
+                                    wallpaperTargets);
+                        }
+                    }, mContext, MAIN_EXECUTOR.getHandler(),
                     mAnimationProvider.getRecentsLaunchDuration());
         }
 
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 0269e4a..25a3078 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -453,8 +453,11 @@
                 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we should
                 // not interrupt it. QuickSwitch assumes that interruption can only happen if the
                 // next gesture is also quick switch.
-                mUncheckedConsumer = new AssistantInputConsumer(this, newGestureState,
-                        InputConsumer.NO_OP, mInputMonitorCompat);
+                mUncheckedConsumer = new AssistantInputConsumer(
+                    this,
+                    newGestureState,
+                    InputConsumer.NO_OP, mInputMonitorCompat,
+                    mOverviewComponentObserver.assistantGestureIsConstrained());
             } else {
                 mUncheckedConsumer = InputConsumer.NO_OP;
             }
@@ -494,7 +497,12 @@
         }
         if (mDeviceState.isFullyGesturalNavMode()) {
             if (mDeviceState.canTriggerAssistantAction(event)) {
-                base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat);
+                base = new AssistantInputConsumer(
+                    this,
+                    newGestureState,
+                    base,
+                    mInputMonitorCompat,
+                    mOverviewComponentObserver.assistantGestureIsConstrained());
             }
 
             if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 94126ff..89e6931 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -90,12 +90,18 @@
     private final float mSquaredSlop;
     private final Context mContext;
     private final GestureDetector mGestureDetector;
+    private final boolean mIsAssistGestureConstrained;
 
-    public AssistantInputConsumer(Context context, GestureState gestureState,
-            InputConsumer delegate, InputMonitorCompat inputMonitor) {
+    public AssistantInputConsumer(
+            Context context,
+            GestureState gestureState,
+            InputConsumer delegate,
+            InputMonitorCompat inputMonitor,
+            boolean isAssistGestureConstrained) {
         super(delegate, inputMonitor);
         final Resources res = context.getResources();
         mContext = context;
+        mIsAssistGestureConstrained = isAssistGestureConstrained;
         mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
         mFlingDistThreshold = res.getDimension(R.dimen.gestures_assistant_fling_threshold);
         mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
@@ -258,7 +264,8 @@
     private class AssistantGestureListener extends SimpleOnGestureListener {
         @Override
         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-            if (isValidAssistantGestureAngle(velocityX, -velocityY)
+            if (!mIsAssistGestureConstrained
+                && isValidAssistantGestureAngle(velocityX, -velocityY)
                     && mDistance >= mFlingDistThreshold
                     && !mLaunchedAssistant
                     && mState != STATE_DELEGATE_ACTIVE) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 681ce02..d9e9cc7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -50,6 +50,7 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
 import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
@@ -119,7 +120,7 @@
 
     private void updateSourceStack(RemoteAnimationTargetCompat target) {
         mSourceInsets.set(target.contentInsets);
-        mSourceStackBounds.set(target.sourceContainerBounds);
+        mSourceStackBounds.set(target.screenSpaceBounds);
 
         // TODO: Should sourceContainerBounds already have this offset?
         mSourceStackBounds.offsetTo(target.position.x, target.position.y);
@@ -199,9 +200,17 @@
         for (int i = 0; i < params.mTargetSet.unfilteredApps.length; i++) {
             RemoteAnimationTargetCompat app = params.mTargetSet.unfilteredApps[i];
             SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
-            mTmpMatrix.setTranslate(app.position.x, app.position.y);
+            if (app.localBounds != null) {
+                mTmpMatrix.setTranslate(0, 0);
+                if (app.activityType == ACTIVITY_TYPE_HOME && app.mode == MODE_CLOSING) {
+                    mTmpMatrix.setTranslate(app.localBounds.left, app.localBounds.top);
+                }
+            } else {
+                mTmpMatrix.setTranslate(app.position.x, app.position.y);
+            }
+
             Rect crop = mTmpRect;
-            crop.set(app.sourceContainerBounds);
+            crop.set(app.screenSpaceBounds);
             crop.offsetTo(0, 0);
             float alpha;
             float cornerRadius = 0f;
@@ -211,7 +220,11 @@
                 alpha = mTaskAlphaCallback.getAlpha(app, params.mTargetAlpha);
                 if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
                     mTmpMatrix.setRectToRect(mSourceRect, mCurrentRect, ScaleToFit.FILL);
-                    mTmpMatrix.postTranslate(app.position.x, app.position.y);
+                    if (app.localBounds != null) {
+                        mTmpMatrix.postTranslate(app.localBounds.left, app.localBounds.top);
+                    } else {
+                        mTmpMatrix.postTranslate(app.position.x, app.position.y);
+                    }
                     mCurrentClipRectF.roundOut(crop);
                     if (mSupportsRoundedCornersOnWindows) {
                         if (params.mCornerRadius > -1) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index e674433..3ed7530 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -287,7 +287,8 @@
 
     @Override
     protected boolean supportsVerticalLandscape() {
-        return FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get();
+        return FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()
+                && !mOrientationState.areMultipleLayoutOrientationsDisabled();
     }
 
     @Override
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 2f45af1..8322d8c 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
@@ -169,7 +169,6 @@
                 }
             };
 
-    private final OrientationEventListener mOrientationListener;
     private int mPreviousRotation;
     protected RecentsAnimationController mRecentsAnimationController;
     protected RecentsAnimationTargets mRecentsAnimationTargets;
@@ -377,22 +376,6 @@
 
         // Initialize quickstep specific cache params here, as this is constructed only once
         mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
-        mOrientationListener = new OrientationEventListener(getContext()) {
-            @Override
-            public void onOrientationChanged(int i) {
-                int rotation = RotationHelper.getRotationFromDegrees(i);
-                if (mPreviousRotation != rotation) {
-                    animateRecentsRotationInPlace(rotation);
-                    if (rotation == 0) {
-                        showActionsView();
-                    } else {
-                        hideActionsView();
-                    }
-                    mPreviousRotation = rotation;
-                }
-            }
-        };
-
     }
 
     public OverScroller getScroller() {
@@ -521,13 +504,6 @@
     }
 
     public void setOverviewStateEnabled(boolean enabled) {
-        if (supportsVerticalLandscape() && mOrientationListener.canDetectOrientation()) {
-            if (enabled) {
-                mOrientationListener.enable();
-            } else {
-                mOrientationListener.disable();
-            }
-        }
         mOverviewStateEnabled = enabled;
         updateTaskStackListenerState();
         if (!enabled) {
@@ -968,35 +944,6 @@
         setSwipeDownShouldLaunchApp(true);
     }
 
-    private void animateRecentsRotationInPlace(int newRotation) {
-        if (!supportsVerticalLandscape()) {
-            return;
-        }
-
-        AnimatorSet pa = setRecentsChangedOrientation(true);
-        pa.addListener(AnimationSuccessListener.forRunnable(() -> {
-            updateLayoutRotation(newRotation);
-            ((DragLayer) mActivity.getDragLayer()).recreateControllers();
-            rotateAllChildTasks();
-            setRecentsChangedOrientation(false).start();
-        }));
-        pa.start();
-    }
-
-    public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) {
-        getRunningTaskIndex();
-        int runningIndex = getCurrentPage();
-        AnimatorSet as = new AnimatorSet();
-        for (int i = 0; i < getTaskViewCount(); i++) {
-            if (runningIndex == i) {
-                continue;
-            }
-            View taskView = getTaskViewAt(i);
-            as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1));
-        }
-        return as;
-    }
-
     abstract protected boolean supportsVerticalLandscape();
 
     private void rotateAllChildTasks() {
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 809543a..a7d00c5 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -52,10 +52,12 @@
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.stream.Stream;
 
@@ -262,17 +264,21 @@
     public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
         QuickstepAppTransitionManagerImpl appTransitionManager =
                 (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
-        appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
+        appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
+            @Override
+            public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+                    RemoteAnimationTargetCompat[] wallpaperTargets) {
 
-            // On the first call clear the reference.
-            signal.cancel();
+                // On the first call clear the reference.
+                signal.cancel();
 
-            ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
-            fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
-                    wallpaperTargets));
-            AnimatorSet anim = new AnimatorSet();
-            anim.play(fadeAnimation);
-            return anim;
+                ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
+                fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
+                        wallpaperTargets));
+                AnimatorSet anim = new AnimatorSet();
+                anim.play(fadeAnimation);
+                return anim;
+            }
         }, signal);
     }
 
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 31c1acf..2cb23f1 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -34,7 +34,8 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 @TargetApi(Build.VERSION_CODES.P)
-public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
+public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat,
+        WrappedAnimationRunnerImpl {
 
     private final Handler mHandler;
     private final boolean mStartAtFrontOfQueue;
@@ -49,6 +50,10 @@
         mStartAtFrontOfQueue = startAtFrontOfQueue;
     }
 
+    public Handler getHandler() {
+        return mHandler;
+    }
+
     // Called only in R+ platform
     @BinderThread
     public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 96340b2..fbd7a8a 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3;
 
+import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
@@ -25,6 +26,7 @@
 import com.android.launcher3.util.ActivityTracker;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.function.BiPredicate;
 
@@ -51,17 +53,21 @@
             // Set a one-time animation provider. After the first call, this will get cleared.
             // TODO: Probably also check the intended target id.
             CancellationSignal cancellationSignal = new CancellationSignal();
-            appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
+            appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
+                @Override
+                public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+                        RemoteAnimationTargetCompat[] wallpaperTargets) {
 
-                // On the first call clear the reference.
-                cancellationSignal.cancel();
-                RemoteAnimationProvider provider = mRemoteAnimationProvider;
-                mRemoteAnimationProvider = null;
+                    // On the first call clear the reference.
+                    cancellationSignal.cancel();
+                    RemoteAnimationProvider provider = mRemoteAnimationProvider;
+                    mRemoteAnimationProvider = null;
 
-                if (provider != null && launcher.getStateManager().getState().overviewUi) {
-                    return provider.createWindowAnimation(appTargets, wallpaperTargets);
+                    if (provider != null && launcher.getStateManager().getState().overviewUi) {
+                        return provider.createWindowAnimation(appTargets, wallpaperTargets);
+                    }
+                    return null;
                 }
-                return null;
             }, cancellationSignal);
         }
         launcher.deferOverlayCallbacksUntilNextResumeOrStop();
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index f691359..b9bd6b1 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -49,6 +49,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
@@ -86,8 +87,6 @@
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
-import java.lang.ref.WeakReference;
-
 /**
  * {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
  * home and/or all-apps.  Not used for 3p launchers.
@@ -299,8 +298,12 @@
         if (mLauncher.isInMultiWindowMode()) {
             for (RemoteAnimationTargetCompat target : appTargets) {
                 if (target.mode == MODE_OPENING) {
-                    bounds.set(target.sourceContainerBounds);
-                    bounds.offsetTo(target.position.x, target.position.y);
+                    bounds.set(target.screenSpaceBounds);
+                    if (target.localBounds != null) {
+                        bounds.set(target.localBounds);
+                    } else {
+                        bounds.offsetTo(target.position.x, target.position.y);
+                    }
                     return bounds;
                 }
             }
@@ -460,6 +463,7 @@
         RectF targetBounds = new RectF(windowTargetBounds);
         RectF currentBounds = new RectF();
         RectF temp = new RectF();
+        Point tmpPos = new Point();
 
         AnimatorSet animatorSet = new AnimatorSet();
         ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
@@ -547,6 +551,13 @@
                 for (int i = appTargets.length - 1; i >= 0; i--) {
                     RemoteAnimationTargetCompat target = appTargets[i];
                     SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+
+                    tmpPos.set(target.position.x, target.position.y);
+                    if (target.localBounds != null) {
+                        final Rect localBounds = target.localBounds;
+                        tmpPos.set(target.localBounds.left, target.localBounds.top);
+                    }
+
                     if (target.mode == MODE_OPENING) {
                         matrix.setScale(scale, scale);
                         matrix.postTranslate(transX0, transY0);
@@ -563,9 +574,9 @@
                                 .withAlpha(1f - mIconAlpha.value)
                                 .withCornerRadius(mWindowRadius.value);
                     } else {
-                        matrix.setTranslate(target.position.x, target.position.y);
+                        matrix.setTranslate(tmpPos.x, tmpPos.y);
                         builder.withMatrix(matrix)
-                                .withWindowCrop(target.sourceContainerBounds)
+                                .withWindowCrop(target.screenSpaceBounds)
                                 .withAlpha(1f);
                     }
                     builder.withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING));
@@ -662,7 +673,7 @@
                     RemoteAnimationTargetCompat target = appTargets[i];
                     params[i] = new SurfaceParams.Builder(target.leash)
                             .withAlpha(1f)
-                            .withWindowCrop(target.sourceContainerBounds)
+                            .withWindowCrop(target.screenSpaceBounds)
                             .withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING))
                             .withCornerRadius(cornerRadius)
                             .build();
@@ -681,6 +692,7 @@
         SyncRtSurfaceTransactionApplierCompat surfaceApplier =
                 new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
         Matrix matrix = new Matrix();
+        Point tmpPos = new Point();
         ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
         int duration = CLOSING_TRANSITION_DURATION_MS;
         float windowCornerRadius = mDeviceProfile.isMultiWindowMode
@@ -697,22 +709,28 @@
                 for (int i = appTargets.length - 1; i >= 0; i--) {
                     RemoteAnimationTargetCompat target = appTargets[i];
                     SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+
+                    tmpPos.set(target.position.x, target.position.y);
+                    if (target.localBounds != null) {
+                        tmpPos.set(target.localBounds.left, target.localBounds.top);
+                    }
+
                     if (target.mode == MODE_CLOSING) {
                         matrix.setScale(mScale.value, mScale.value,
-                                target.sourceContainerBounds.centerX(),
-                                target.sourceContainerBounds.centerY());
+                                target.screenSpaceBounds.centerX(),
+                                target.screenSpaceBounds.centerY());
                         matrix.postTranslate(0, mDy.value);
-                        matrix.postTranslate(target.position.x, target.position.y);
+                        matrix.postTranslate(tmpPos.x, tmpPos.y);
                         builder.withMatrix(matrix)
                                 .withAlpha(mAlpha.value)
                                 .withCornerRadius(windowCornerRadius);
                     } else {
-                        matrix.setTranslate(target.position.x, target.position.y);
+                        matrix.setTranslate(tmpPos.x, tmpPos.y);
                         builder.withMatrix(matrix)
                                 .withAlpha(1f);
                     }
                     params[i] = builder
-                            .withWindowCrop(target.sourceContainerBounds)
+                            .withWindowCrop(target.screenSpaceBounds)
                             .withLayer(RemoteAnimationProvider.getLayer(target, MODE_CLOSING))
                             .build();
                 }
@@ -785,52 +803,6 @@
     }
 
     /**
-     * Used with WrappedLauncherAnimationRunner as an interface for the runner to call back to the
-     * implementation.
-     */
-    protected interface WrappedAnimationRunnerImpl {
-        Handler getHandler();
-        void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets,
-                LauncherAnimationRunner.AnimationResult result);
-    }
-
-    /**
-     * This class is needed to wrap any animation runner that is a part of the
-     * RemoteAnimationDefinition:
-     * - Launcher creates a new instance of the LauncherAppTransitionManagerImpl whenever it is
-     *   created, which in turn registers a new definition
-     * - When the definition is registered, window manager retains a strong binder reference to the
-     *   runner passed in
-     * - If the Launcher activity is recreated, the new definition registered will replace the old
-     *   reference in the system's activity record, but until the system server is GC'd, the binder
-     *   reference will still exist, which references the runner in the Launcher process, which
-     *   references the (old) Launcher activity through this class
-     *
-     * Instead we make the runner provided to the definition static only holding a weak reference to
-     * the runner implementation.  When this animation manager is destroyed, we remove the Launcher
-     * reference to the runner, leaving only the weak ref from the runner.
-     */
-    protected static class WrappedLauncherAnimationRunner<R extends WrappedAnimationRunnerImpl>
-            extends LauncherAnimationRunner {
-        private WeakReference<R> mImpl;
-
-        public WrappedLauncherAnimationRunner(R animationRunnerImpl, boolean startAtFrontOfQueue) {
-            super(animationRunnerImpl.getHandler(), startAtFrontOfQueue);
-            mImpl = new WeakReference<>(animationRunnerImpl);
-        }
-
-        @Override
-        public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
-            R animationRunnerImpl = mImpl.get();
-            if (animationRunnerImpl != null) {
-                animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
-            }
-        }
-    }
-
-    /**
      * Remote animation runner for animation from the app to Launcher, including recents.
      */
     protected class WallpaperOpenLauncherAnimationRunner implements WrappedAnimationRunnerImpl {
diff --git a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
new file mode 100644
index 0000000..da2aee4
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.os.Handler;
+
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Used with WrappedLauncherAnimationRunner as an interface for the runner to call back to the
+ * implementation.
+ */
+public interface WrappedAnimationRunnerImpl {
+    Handler getHandler();
+    void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            LauncherAnimationRunner.AnimationResult result);
+}
diff --git a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
new file mode 100644
index 0000000..1753b62
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This class is needed to wrap any animation runner that is a part of the
+ * RemoteAnimationDefinition:
+ * - Launcher creates a new instance of the LauncherAppTransitionManagerImpl whenever it is
+ *   created, which in turn registers a new definition
+ * - When the definition is registered, window manager retains a strong binder reference to the
+ *   runner passed in
+ * - If the Launcher activity is recreated, the new definition registered will replace the old
+ *   reference in the system's activity record, but until the system server is GC'd, the binder
+ *   reference will still exist, which references the runner in the Launcher process, which
+ *   references the (old) Launcher activity through this class
+ *
+ * Instead we make the runner provided to the definition static only holding a weak reference to
+ * the runner implementation.  When this animation manager is destroyed, we remove the Launcher
+ * reference to the runner, leaving only the weak ref from the runner.
+ */
+public class WrappedLauncherAnimationRunner<R extends WrappedAnimationRunnerImpl>
+        extends LauncherAnimationRunner {
+    private WeakReference<R> mImpl;
+
+    public WrappedLauncherAnimationRunner(R animationRunnerImpl, boolean startAtFrontOfQueue) {
+        super(animationRunnerImpl.getHandler(), startAtFrontOfQueue);
+        mImpl = new WeakReference<>(animationRunnerImpl);
+    }
+
+    @Override
+    public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+        R animationRunnerImpl = mImpl.get();
+        if (animationRunnerImpl != null) {
+            animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java b/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java
new file mode 100644
index 0000000..548223a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Size;
+import android.view.View;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.graphics.LauncherPreviewRenderer;
+import com.android.systemui.shared.system.SurfaceViewRequestReceiver;
+
+/** Render preview using surface view. */
+public class PreviewSurfaceRenderer {
+
+    /** Handle a received surface view request. */
+    public static void render(Context context, Bundle bundle) {
+        final String gridName = bundle.getString("name");
+        bundle.remove("name");
+        final InvariantDeviceProfile idp = new InvariantDeviceProfile(context, gridName);
+
+        MAIN_EXECUTOR.execute(() -> {
+            View view = new LauncherPreviewRenderer(context, idp).getRenderedView();
+            new SurfaceViewRequestReceiver().onReceive(context, bundle, view,
+                    new Size(view.getMeasuredWidth(), view.getMeasuredHeight()));
+        });
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 866836e..9edc86e 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -22,6 +22,7 @@
 
 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -104,6 +105,10 @@
         updateOverviewTargets();
     }
 
+    public boolean assistantGestureIsConstrained() {
+        return (mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
+    }
+
     /**
      * Update overview intent and {@link BaseActivityInterface} based off the current launcher home
      * component.
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 6520c4f..21b97ec 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -21,21 +21,22 @@
 import android.os.Handler;
 
 import com.android.launcher3.LauncherAnimationRunner;
+import com.android.launcher3.WrappedLauncherAnimationRunner;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.TransactionCompat;
 
-@FunctionalInterface
-public interface RemoteAnimationProvider {
+public abstract class RemoteAnimationProvider {
 
+    LauncherAnimationRunner mAnimationRunner;
     static final int Z_BOOST_BASE = 800570000;
 
-    AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+    public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets);
 
-    default ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
-        LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
+    ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
+        mAnimationRunner = new LauncherAnimationRunner(handler,
                 false /* startAtFrontOfQueue */) {
 
             @Override
@@ -44,8 +45,11 @@
                 result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
             }
         };
+        final LauncherAnimationRunner wrapper = new WrappedLauncherAnimationRunner(
+                mAnimationRunner, false /* startAtFrontOfQueue */);
+
         return ActivityOptionsCompat.makeRemoteAnimation(
-                new RemoteAnimationAdapterCompat(runner, duration, 0));
+                new RemoteAnimationAdapterCompat(wrapper, duration, 0));
     }
 
     /**
@@ -63,7 +67,7 @@
         }
     }
 
-    static int getLayer(RemoteAnimationTargetCompat target, int boostModeTarget) {
+    public static int getLayer(RemoteAnimationTargetCompat target, int boostModeTarget) {
         return target.mode == boostModeTarget
                 ? Z_BOOST_BASE + target.prefixOrderIndex
                 : target.prefixOrderIndex;
@@ -72,7 +76,7 @@
     /**
      * @return the target with the lowest opaque layer for a certain app animation, or null.
      */
-    static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
+    public static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
             RemoteAnimationTargetCompat[] appTargets, int mode) {
         int lowestLayer = Integer.MAX_VALUE;
         int lowestLayerIndex = -1;
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 4ac815e..0afe4a8 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -166,12 +166,9 @@
             return false;
         }
 
-        setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
-                overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
-        setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
-                overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY);
-        setOverlayPackageEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY,
-                overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY);
+        Log.d(TAG, "setActiveOverlay: " + overlayPackage + "...");
+        UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+                "cmd overlay enable-exclusive " + overlayPackage);
 
         if (currentSysUiNavigationMode() != expectedMode) {
             final CountDownLatch latch = new CountDownLatch(1);
@@ -210,14 +207,6 @@
         return true;
     }
 
-    private static void setOverlayPackageEnabled(String overlayPackage, boolean enable)
-            throws Exception {
-        Log.d(TAG, "setOverlayPackageEnabled: " + overlayPackage + " " + enable);
-        final String action = enable ? "enable" : "disable";
-        UiDevice.getInstance(getInstrumentation()).executeShellCommand(
-                "cmd overlay " + action + " " + overlayPackage);
-    }
-
     private static boolean packageExists(String packageName) {
         try {
             PackageManager pm = getInstrumentation().getContext().getPackageManager();
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 471a743..8453c41 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -100,11 +100,11 @@
             "Suggests folder names instead of blank text.");
 
     public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
-            "APP_SEARCH_IMPROVEMENTS", false,
+            "APP_SEARCH_IMPROVEMENTS", true,
             "Adds localized title and keyword search and ranking");
 
-    public static final BooleanFlag ENABLE_PREDICTION_DISMISS = new DeviceFlag(
-            "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list");
+    public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
+            "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
 
     public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
             "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
@@ -117,11 +117,11 @@
             "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
 
-    public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
+    public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
             "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
 
-    public static final BooleanFlag HOTSEAT_MIGRATE_NEW_PAGE = new DeviceFlag(
-            "HOTSEAT_MIGRATE_NEW_PAGE", true,
+    public static final BooleanFlag HOTSEAT_MIGRATE_NEW_PAGE = getDebugFlag(
+            "HOTSEAT_MIGRATE_NEW_PAGE", false,
             "Migrates hotseat to a new workspace page instead of same page");
 
     public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
@@ -131,13 +131,18 @@
             "MULTI_DB_GRID_MIRATION_ALGO", false, "Use the multi-db grid migration algorithm");
 
     public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
-            "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true,
-            "Show launcher preview in grid picker");
+            "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker");
+
+    public static final BooleanFlag USE_SURFACE_VIEW_FOR_GRID_PREVIEW = getDebugFlag(
+            "USE_SURFACE_VIEW_FOR_GRID_PREVIEW", false, "Use surface view for grid preview");
 
     public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
             "ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview."
             + " As part of this decoupling, also distinguish swipe up from nav bar vs above it.");
 
+    public static final BooleanFlag ENABLE_SELECT_MODE = getDebugFlag(
+            "ENABLE_SELECT_MODE", true, "Show Select Mode button in Overview Actions");
+
     public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
             "ENABLE_DATABASE_RESTORE", true,
             "Enable database restore when new restore session is created");
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 184dbb9..07161da 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -15,8 +15,10 @@
  */
 package com.android.launcher3.folder;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.os.Process;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -34,12 +36,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-import java.util.function.Predicate;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -101,27 +100,23 @@
         }
         // If all the icons are from work profile,
         // Then, suggest "Work" as the folder name
-        List<WorkspaceItemInfo> distinctItemInfos = workspaceItemInfos.stream()
-                .filter(distinctByKey(p -> p.user))
-                .collect(Collectors.toList());
-
-        if (distinctItemInfos.size() == 1
-                && !distinctItemInfos.get(0).user.equals(Process.myUserHandle())) {
-            // Place it as last viable suggestion
+        Set<UserHandle> users = workspaceItemInfos.stream().map(w -> w.user)
+                .collect(Collectors.toSet());
+        if (users.size() == 1 && !users.contains(Process.myUserHandle())) {
             setAsLastSuggestion(nameInfos,
                     context.getResources().getString(R.string.work_folder_name));
         }
 
         // If all the icons are from same package (e.g., main icon, shortcut, shortcut)
         // Then, suggest the package's title as the folder name
-        distinctItemInfos = workspaceItemInfos.stream()
-                .filter(distinctByKey(p -> p.getTargetComponent() != null
-                        ? p.getTargetComponent().getPackageName() : ""))
-                .collect(Collectors.toList());
+        Set<String> packageNames = workspaceItemInfos.stream()
+                .map(WorkspaceItemInfo::getTargetComponent)
+                .filter(Objects::nonNull)
+                .map(ComponentName::getPackageName)
+                .collect(Collectors.toSet());
 
-        if (distinctItemInfos.size() == 1) {
-            Optional<AppInfo> info = getAppInfoByPackageName(
-                    distinctItemInfos.get(0).getTargetComponent().getPackageName());
+        if (packageNames.size() == 1) {
+            Optional<AppInfo> info = getAppInfoByPackageName(packageNames.iterator().next());
             // Place it as first viable suggestion and shift everything else
             info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
         }
@@ -135,6 +130,7 @@
             return Optional.empty();
         }
         return mAppInfos.stream()
+                .filter(info -> info.componentName != null)
                 .filter(info -> info.componentName.getPackageName().equals(packageName))
                 .findAny();
     }
@@ -174,12 +170,6 @@
                         label.toString()));
     }
 
-    // This method can be moved to some Utility class location.
-    private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
-        Map<Object, Boolean> map = new ConcurrentHashMap<>();
-        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
-    }
-
     private class FolderNameWorker extends BaseModelUpdateTask {
         @Override
         public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
diff --git a/src/com/android/launcher3/graphics/GridOptionsProvider.java b/src/com/android/launcher3/graphics/GridOptionsProvider.java
index 71b4366..607aba9 100644
--- a/src/com/android/launcher3/graphics/GridOptionsProvider.java
+++ b/src/com/android/launcher3/graphics/GridOptionsProvider.java
@@ -1,5 +1,6 @@
 package com.android.launcher3.graphics;
 
+import static com.android.launcher3.config.FeatureFlags.USE_SURFACE_VIEW_FOR_GRID_PREVIEW;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import android.content.ContentProvider;
@@ -19,6 +20,7 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.GridOption;
 import com.android.launcher3.R;
+import com.android.launcher3.uioverrides.PreviewSurfaceRenderer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -62,6 +64,11 @@
     private static final String KEY_PREVIEW = "preview";
     private static final String MIME_TYPE_PNG = "image/png";
 
+    private static final String METHOD_GET_PREVIEW = "get_preview";
+    private static final String METADATA_KEY_PREVIEW_VERSION = "preview_version";
+
+
+
     public static final PipeDataWriter<Future<Bitmap>> BITMAP_WRITER =
             new PipeDataWriter<Future<Bitmap>>() {
                 @Override
@@ -98,6 +105,10 @@
                     .add(KEY_IS_DEFAULT, idp.numColumns == gridOption.numColumns
                             && idp.numRows == gridOption.numRows);
         }
+        Bundle metadata = new Bundle();
+        metadata.putString(METADATA_KEY_PREVIEW_VERSION,
+                USE_SURFACE_VIEW_FOR_GRID_PREVIEW.get() ? "V2" : "V1");
+        cursor.setExtras(metadata);
         return cursor;
     }
 
@@ -188,4 +199,14 @@
             throw new FileNotFoundException(e.getMessage());
         }
     }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras)  {
+        if (!METHOD_GET_PREVIEW.equals(method)) {
+            return null;
+        }
+
+        PreviewSurfaceRenderer.render(getContext(), extras);
+        return null;
+    }
 }
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 02e98e8..5bc6610 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -262,6 +262,13 @@
         });
     }
 
+    /** Populate preview and render it. */
+    public View getRenderedView() {
+        MainThreadRenderer renderer = new MainThreadRenderer(mContext);
+        renderer.populate();
+        return renderer.mRootView;
+    }
+
     private class MainThreadRenderer extends ContextThemeWrapper
             implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
 
@@ -388,7 +395,7 @@
             }
         }
 
-        private void renderScreenShot(Canvas canvas) {
+        private void populate() {
             if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
                 boolean needsToMigrate = needsToMigrate(mContext, mIdp);
                 boolean success = false;
@@ -499,7 +506,10 @@
             measureView(mRootView, mDp.widthPx, mDp.heightPx);
             // Additional measure for views which use auto text size API
             measureView(mRootView, mDp.widthPx, mDp.heightPx);
+        }
 
+        private void renderScreenShot(Canvas canvas) {
+            populate();
             mRootView.draw(canvas);
             dispatchVisibilityAggregated(mRootView, false);
         }
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 513bf62..3770d17 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -25,6 +25,9 @@
 import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
 import static com.android.launcher3.logging.LoggerUtils.newTarget;
 import static com.android.launcher3.logging.LoggerUtils.newTouchAction;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.TipType;
 
 import static java.util.Optional.ofNullable;
 
@@ -48,7 +51,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -57,8 +60,11 @@
 import com.android.launcher3.util.LogConfig;
 import com.android.launcher3.util.ResourceBasedOverride;
 
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+import com.google.protobuf.nano.MessageNano;
+
 import java.util.ArrayList;
-import java.util.Locale;
 import java.util.UUID;
 
 /**
@@ -162,7 +168,7 @@
             // Direction DOWN means the task was launched, UP means it was dismissed.
             event.action.dir = direction;
         }
-        event.srcTarget[0].itemType = LauncherLogProto.ItemType.TASK;
+        event.srcTarget[0].itemType = ItemType.TASK;
         event.srcTarget[0].pageIndex = taskIndex;
         fillComponentInfo(event.srcTarget[0], componentKey.componentName);
         dispatchUserEvent(event, null);
@@ -289,7 +295,7 @@
     public void logActionBounceTip(int containerType) {
         LauncherEvent event = newLauncherEvent(newAction(Action.Type.TIP),
                 newContainerTarget(containerType));
-        event.srcTarget[0].tipType = LauncherLogProto.TipType.BOUNCE;
+        event.srcTarget[0].tipType = TipType.BOUNCE;
         dispatchUserEvent(event, null);
     }
 
@@ -316,7 +322,7 @@
             int srcChildTargetType, int srcParentContainerType, int dstContainerType,
             int pageIndex) {
         LauncherEvent event;
-        if (srcChildTargetType == LauncherLogProto.ItemType.TASK) {
+        if (srcChildTargetType == ItemType.TASK) {
             event = newLauncherEvent(newTouchAction(action),
                     newItemTarget(srcChildTargetType),
                     newContainerTarget(srcParentContainerType));
@@ -374,10 +380,11 @@
                 .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
                 .setElapsedSessionMillis(
                         SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
-        if (!IS_VERBOSE) {
-            return;
+        try {
+            dispatchUserEvent(LauncherEvent.parseFrom(launcherEvent.toByteArray()), null);
+        } catch (InvalidProtocolBufferNanoException e) {
+            throw new RuntimeException("Cannot convert LauncherEvent from Lite to Nano version.");
         }
-        Log.d(TAG, launcherEvent.toString());
     }
 
     public void logDeepShortcutsOpen(View icon) {
@@ -421,8 +428,8 @@
         action.command = Action.Command.BACK;
         action.dir = isButton ? Action.Direction.NONE :
                 gestureSwipeLeft ? Action.Direction.LEFT : Action.Direction.RIGHT;
-        Target target = newControlTarget(isButton ? LauncherLogProto.ControlType.BACK_BUTTON :
-                LauncherLogProto.ControlType.BACK_GESTURE);
+        Target target = newControlTarget(isButton ? ControlType.BACK_BUTTON :
+                ControlType.BACK_GESTURE);
         target.spanX = downX;
         target.spanY = downY;
         target.cardinality = completed ? 1 : 0;
@@ -471,36 +478,14 @@
         if (!IS_VERBOSE) {
             return;
         }
-        Log.d(TAG, generateLog(ev));
-    }
-
-    /**
-     * Returns a human-readable log for given user event.
-     */
-    public static String generateLog(LauncherEvent ev) {
-        String log = "\n-----------------------------------------------------"
-                + "\naction:" + LoggerUtils.getActionStr(ev.action);
-        if (ev.srcTarget != null && ev.srcTarget.length > 0) {
-            log += "\n Source " + getTargetsStr(ev.srcTarget);
+        LauncherLogProto.LauncherEvent liteLauncherEvent;
+        try {
+            liteLauncherEvent =
+                    LauncherLogProto.LauncherEvent.parseFrom(MessageNano.toByteArray(ev));
+        } catch (InvalidProtocolBufferException e) {
+            throw new RuntimeException("Cannot parse LauncherEvent from Nano to Lite version");
         }
-        if (ev.destTarget != null && ev.destTarget.length > 0) {
-            log += "\n Destination " + getTargetsStr(ev.destTarget);
-        }
-        log += String.format(Locale.US,
-                "\n Elapsed container %d ms, session %d ms, action %d ms",
-                ev.elapsedContainerMillis,
-                ev.elapsedSessionMillis,
-                ev.actionDurationMillis);
-        log += "\n\n";
-        return log;
-    }
-
-    private static String getTargetsStr(Target[] targets) {
-        String result = "child:" + LoggerUtils.getTargetStr(targets[0]);
-        for (int i = 1; i < targets.length; i++) {
-            result += "\tparent:" + LoggerUtils.getTargetStr(targets[i]);
-        }
-        return result;
+        Log.d(TAG, liteLauncherEvent.toString());
     }
 
     /**
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java b/src_ui_overrides/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java
new file mode 100644
index 0000000..4913cad
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import android.content.Context;
+import android.os.Bundle;
+
+/** Render preview using surface view. */
+public class PreviewSurfaceRenderer {
+
+    /** Handle a received surface view request. */
+    public static void render(Context context, Bundle bundle) { }
+}