Merge "Fix scrolling issue in the full widgets picker" into sc-dev
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index d7bcd9e..f303f31 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -15,12 +15,6 @@
 -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <com.android.quickstep.views.SplitPlaceholderView
-        android:id="@+id/split_placeholder"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/split_placeholder_size"
-        android:background="@android:color/darker_gray"
-        android:visibility="gone" />
 
     <com.android.quickstep.views.LauncherRecentsView
         android:id="@+id/overview_panel"
@@ -31,6 +25,13 @@
         android:clipToPadding="false"
         android:visibility="invisible" />
 
+    <com.android.quickstep.views.SplitPlaceholderView
+        android:id="@+id/split_placeholder"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/split_placeholder_size"
+        android:background="@android:color/darker_gray"
+        android:visibility="gone" />
+
     <include
         android:id="@+id/overview_actions_view"
         layout="@layout/overview_actions_container" />
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 01616d4..b3374f3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -22,12 +22,12 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
 import android.util.FloatProperty;
@@ -97,10 +97,10 @@
         PagedOrientationHandler orientationHandler =
                 ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
         FloatProperty taskViewsFloat = orientationHandler.getSplitSelectTaskOffset(
-                TASK_PRIMARY_TRANSLATION, TASK_SECONDARY_TRANSLATION, mLauncher.getDeviceProfile());
+                TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
+                mLauncher.getDeviceProfile());
         setter.setFloat(mRecentsView, taskViewsFloat,
-                toState.getOverviewSecondaryTranslation(mLauncher),
-                config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
+                toState.getOverviewSecondaryTranslation(mLauncher), LINEAR);
 
         setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
                 config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 12de4a6..8278a5a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -36,11 +36,13 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
 import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -207,7 +209,7 @@
     /** Create state animation to control non-overview components. */
     private void updateNonOverviewAnim(LauncherState toState, StateAnimationConfig config) {
         config.duration = (long) (Math.max(mXRange, mYRange) * 2);
-        config.animFlags |= SKIP_OVERVIEW;
+        config.animFlags |= SKIP_OVERVIEW | SKIP_SCRIM;
         mNonOverviewAnim = mLauncher.getStateManager()
                 .createAnimationToNewWorkspace(toState, config);
         mNonOverviewAnim.getTarget().addListener(mClearStateOnCancelListener);
@@ -229,10 +231,14 @@
         // As we drag right, animate the following properties:
         //   - RecentsView translationX
         //   - OverviewScrim
+        //   - RecentsView fade (if it's empty)
         PendingAnimation xAnim = new PendingAnimation((long) (mXRange * 2));
         xAnim.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1], LINEAR);
         xAnim.setViewBackgroundColor(mLauncher.getScrimView(),
                 toState.getWorkspaceScrimColor(mLauncher), LINEAR);
+        if (mRecentsView.getTaskViewCount() == 0) {
+            xAnim.addFloat(mRecentsView, CONTENT_ALPHA, 0f, 1f, LINEAR);
+        }
         mXOverviewAnim = xAnim.createPlaybackController();
         mXOverviewAnim.dispatchOnStart();
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index ecaac94..fe10908 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -47,7 +47,6 @@
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_END;
 import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_START;
-import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
@@ -55,7 +54,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
@@ -340,6 +338,10 @@
     }
 
     protected boolean onActivityInit(Boolean alreadyOnHome) {
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+            return false;
+        }
+
         T createdActivity = mActivityInterface.getCreatedActivity();
         if (createdActivity != null) {
             initTransitionEndpoints(createdActivity.getDeviceProfile());
@@ -464,8 +466,6 @@
                     mDeviceState.getRotationTouchHelper()
                             .onEndTargetCalculated(mGestureState.getEndTarget(),
                                     mActivityInterface);
-
-                    mRecentsView.onGestureEndTargetCalculated(mGestureState.getEndTarget());
                 });
 
         notifyGestureStartedAsync();
@@ -571,6 +571,8 @@
                 }
             });
             reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start();
+            mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
+                    reapplyWindowTransformAnim::cancel);
         } else {
             applyWindowTransform();
         }
@@ -1133,6 +1135,10 @@
             }
             homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
             mLauncherTransitionController = null;
+
+            if (mRecentsView != null) {
+                mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget());
+            }
         } else {
             AnimatorSet animatorSet = new AnimatorSet();
             ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
@@ -1171,9 +1177,9 @@
                 }
             });
             animatorSet.play(windowAnim);
-            S state = mActivityInterface.stateFromGestureEndTarget(mGestureState.getEndTarget());
-            if (mRecentsView != null && state.displayOverviewTasksAsGrid(mDp)) {
-                animatorSet.play(ObjectAnimator.ofFloat(mRecentsView, RECENTS_GRID_PROGRESS, 1));
+            if (mRecentsView != null) {
+                mRecentsView.onPrepareGestureEndAnimation(
+                        animatorSet, mGestureState.getEndTarget());
             }
             animatorSet.setDuration(duration).setInterpolator(interpolator);
             animatorSet.start();
@@ -1376,12 +1382,6 @@
         mActivityInitListener.unregister();
         ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
         mTaskSnapshot = null;
-        mHandler.post(() -> {
-            // Defer clearing the activity since invalidation can happen over multiple callbacks
-            // ie. invalidateHandlerWithLauncher()
-            mActivity = null;
-            mRecentsView = null;
-        });
     }
 
     private void invalidateHandlerWithLauncher() {
@@ -1392,6 +1392,12 @@
 
         mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
         resetLauncherListeners();
+
+        mHandler.post(() -> {
+            // Defer clearing the activity since invalidation can happen over multiple callbacks.
+            mActivity = null;
+            mRecentsView = null;
+        });
     }
 
     private void endLauncherTransitionController() {
@@ -1701,7 +1707,7 @@
         // No need to apply any transform if there is ongoing swipe-pip-to-home animator since
         // that animator handles the leash solely.
         if (mRecentsAnimationTargets != null && !mIsSwipingPipToHome) {
-            if (mRecentsViewScrollLinked) {
+            if (mRecentsViewScrollLinked && mRecentsView != null) {
                 mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
             }
             mTaskViewSimulator.apply(mTransformParams);
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 9398277..46cd8a2 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -260,6 +260,7 @@
             @Override
             public void setAnimation(RectFSpringAnim anim) {
                 anim.addAnimatorListener(floatingWidgetView);
+                floatingWidgetView.setOnTargetChangeListener(anim::onTargetPositionChanged);
                 floatingWidgetView.setFastFinishRunnable(anim::end);
             }
 
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index eee02b9..0e9e3ad 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -22,11 +22,13 @@
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
 import static com.android.launcher3.Utilities.createHomeIntent;
+import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -36,6 +38,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.view.SurfaceControl.Transaction;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -48,6 +51,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.WrappedAnimationRunnerImpl;
 import com.android.launcher3.WrappedLauncherAnimationRunner;
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -92,6 +96,8 @@
 
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
 
+    private static final long HOME_APPEAR_DURATION = 250;
+
     private RecentsDragLayer mDragLayer;
     private ScrimView mScrimView;
     private FallbackRecentsView mFallbackRecentsView;
@@ -114,6 +120,7 @@
         mScrimView = findViewById(R.id.scrim_view);
         mFallbackRecentsView = findViewById(R.id.overview_panel);
         mActionsView = findViewById(R.id.overview_actions_view);
+        SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f);
 
         SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder);
         splitPlaceholderView.init(
@@ -351,7 +358,32 @@
     }
 
     private void startHomeInternal() {
-        startActivity(createHomeIntent());
+        WrappedLauncherAnimationRunner runner = new WrappedLauncherAnimationRunner(
+                getMainThreadHandler(), this::onCreateAnimationToHome, true);
+        RemoteAnimationAdapterCompat adapterCompat =
+                new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0);
+        startActivity(createHomeIntent(),
+                ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
+    }
+
+    private void onCreateAnimationToHome(
+            int transit, RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+        AnimatorPlaybackController controller = getStateManager()
+                .createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
+        controller.dispatchOnStart();
+
+        RemoteAnimationTargets targets = new RemoteAnimationTargets(
+                appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
+        for (RemoteAnimationTargetCompat app : targets.apps) {
+            new Transaction().setAlpha(app.leash.getSurfaceControl(), 1).apply();
+        }
+        AnimatorSet anim = new AnimatorSet();
+        anim.play(controller.getAnimationPlayer());
+        anim.setDuration(HOME_APPEAR_DURATION);
+        result.setAnimation(anim, this,
+                () -> getStateManager().goToState(RecentsState.HOME, false));
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4fc9770..338a6ef 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -496,6 +496,13 @@
             Point sz = new Point();
             display.getRealSize(sz);
             if (rotation != Surface.ROTATION_0) {
+                if ((rotation % 2) != 0) {
+                    // via display-manager, the display size is unrotated, so "rotate" its size
+                    // to match the rotation we are transforming the event into.
+                    final int tmpX = sz.x;
+                    sz.x = sz.y;
+                    sz.y = tmpX;
+                }
                 event.transform(InputChannelCompat.createRotationMatrix(rotation, sz.x, sz.y));
             }
         }
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 4d4b6e1..1bb8e96 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -20,6 +20,7 @@
 import static com.android.quickstep.fallback.RecentsState.HOME;
 import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
 
+import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
@@ -27,6 +28,9 @@
 import android.util.AttributeSet;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.testing.TestProtocol;
@@ -67,7 +71,6 @@
     @Override
     public void startHome() {
         mActivity.startHome();
-        mActivity.getStateManager().goToState(RecentsState.HOME);
     }
 
     /**
@@ -86,14 +89,17 @@
      * to the center.
      */
     @Override
-    public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
-        super.onGestureEndTargetCalculated(endTarget);
-        if (mHomeTaskInfo != null && endTarget == RECENTS) {
+    public void onPrepareGestureEndAnimation(
+            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+        super.onPrepareGestureEndAnimation(animatorSet, endTarget);
+        if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
             TaskView tv = getTaskView(mHomeTaskInfo.taskId);
             if (tv != null) {
                 PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
                 pa.addEndListener(e -> setCurrentTask(-1));
-                runDismissAnimation(pa);
+                AnimatorPlaybackController controller = pa.createPlaybackController();
+                controller.dispatchOnStart();
+                animatorSet.play(controller.getAnimationPlayer());
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index 96c0e48..b6cfdce 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -50,6 +50,7 @@
     public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
             FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN);
     public static final RecentsState HOME = new RecentsState(3, 0);
+    public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
 
     public final int ordinal;
     private final int mFlags;
@@ -161,4 +162,15 @@
             return getOverviewScaleAndOffsetForBackgroundState(activity);
         }
     }
+
+    private static class LauncherState extends RecentsState {
+        LauncherState(int id, int flags) {
+            super(id, flags);
+        }
+
+        @Override
+        public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+            return new float[] { NO_SCALE, 1 };
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 01d51f8..3d33e57 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -25,6 +25,7 @@
 import android.animation.AnimatorSet;
 import android.app.ActivityOptions;
 import android.content.res.Resources;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -64,6 +65,7 @@
     private final SystemUiProxy mSystemUiProxy;
     private TaskView mInitialTaskView;
     private SplitPositionOption mInitialPosition;
+    private Rect mInitialBounds;
     private final Handler mHandler;
 
     public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
@@ -74,9 +76,11 @@
     /**
      * To be called after first task selected
      */
-    public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption) {
+    public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption,
+            Rect initialBounds) {
         mInitialTaskView = taskView;
         mInitialPosition = positionOption;
+        mInitialBounds = initialBounds;
     }
 
     /**
@@ -220,9 +224,14 @@
     public void resetState() {
         mInitialTaskView = null;
         mInitialPosition = null;
+        mInitialBounds = null;
     }
 
     public boolean isSplitSelectActive() {
         return mInitialTaskView != null;
     }
+
+    public Rect getInitialBounds() {
+        return mInitialBounds;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index e63f8bb..f578ad1 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
@@ -95,7 +96,9 @@
         mContext = context;
         mSizeStrategy = sizeStrategy;
 
-        mOrientationState = new RecentsOrientedState(context, sizeStrategy, i -> { });
+        // TODO(b/187074722): Don't create this per-TaskViewSimulator
+        mOrientationState = TraceHelper.allowIpcs("",
+                () -> new RecentsOrientedState(context, sizeStrategy, i -> { }));
         mOrientationState.setGestureActive(true);
         mCurrentFullscreenParams = new FullscreenDrawParams(context);
         mOrientationStateId = mOrientationState.getStateId();
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
index f74aa55..9ea2369 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
@@ -100,8 +100,8 @@
     /** Restores the drawables to the source view. */
     void finish() {
         if (isUninitialized()) return;
-        mSourceView.setForeground(mOriginalForeground);
-        mSourceView.setBackground(mOriginalBackground);
+        if (mOriginalForeground != null) mSourceView.setForeground(mOriginalForeground);
+        if (mOriginalBackground != null) mSourceView.setBackground(mOriginalBackground);
     }
 
     void recycle() {
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 8499902..ed54f10 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -27,6 +27,7 @@
 import android.view.GhostView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.Launcher;
@@ -39,7 +40,8 @@
 
 /** A view that mimics an App Widget through a launch animation. */
 @TargetApi(Build.VERSION_CODES.S)
-public class FloatingWidgetView extends FrameLayout implements AnimatorListener {
+public class FloatingWidgetView extends FrameLayout implements AnimatorListener,
+        OnGlobalLayoutListener {
     private static final Matrix sTmpMatrix = new Matrix();
 
     private final Launcher mLauncher;
@@ -54,6 +56,7 @@
 
     private Runnable mEndRunnable;
     private Runnable mFastFinishRunnable;
+    private Runnable mOnTargetChangeRunnable;
 
     public FloatingWidgetView(Context context) {
         this(context, null);
@@ -93,6 +96,32 @@
     public void onAnimationRepeat(Animator animator) {
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        getViewTreeObserver().addOnGlobalLayoutListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        getViewTreeObserver().removeOnGlobalLayoutListener(this);
+        super.onDetachedFromWindow();
+    }
+
+    @Override
+    public void onGlobalLayout() {
+        if (isUninitialized()) return;
+        positionViews();
+        if (mOnTargetChangeRunnable != null) {
+            mOnTargetChangeRunnable.run();
+        }
+    }
+
+    /** Sets a runnable that is called on global layout change. */
+    public void setOnTargetChangeListener(Runnable onTargetChangeListener) {
+        mOnTargetChangeRunnable = onTargetChangeListener;
+    }
+
     /** Sets a runnable that is called after a call to {@link #fastFinish()}. */
     public void setFastFinishRunnable(Runnable runnable) {
         mFastFinishRunnable = runnable;
@@ -205,6 +234,7 @@
     private void recycle() {
         mEndRunnable = null;
         mFastFinishRunnable = null;
+        mOnTargetChangeRunnable = null;
         mBackgroundPosition = null;
         mListenerView.setListener(null);
         mAppWidgetView = null;
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index f5a8ff8..2c5f661 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -245,8 +245,8 @@
         if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
             // We want to keep the tasks translations in this temporary state
             // after resetting the rest above
-            setTaskViewsResistanceTranslation(mTaskViewsSecondaryTranslation);
-            setTaskViewsPrimaryTranslation(mTaskViewsPrimaryTranslation);
+            setTaskViewsPrimarySplitTranslation(mTaskViewsPrimarySplitTranslation);
+            setTaskViewsSecondarySplitTranslation(mTaskViewsSecondarySplitTranslation);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 3c59441..63981b1 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -254,16 +254,29 @@
      * more specific, we'd want to create a similar FloatProperty just for a TaskView's
      * offsetX/Y property
      */
-    public static final FloatProperty<RecentsView> TASK_PRIMARY_TRANSLATION =
-            new FloatProperty<RecentsView>("taskPrimaryTranslation") {
+    public static final FloatProperty<RecentsView> TASK_PRIMARY_SPLIT_TRANSLATION =
+            new FloatProperty<RecentsView>("taskPrimarySplitTranslation") {
                 @Override
                 public void setValue(RecentsView recentsView, float v) {
-                    recentsView.setTaskViewsPrimaryTranslation(v);
+                    recentsView.setTaskViewsPrimarySplitTranslation(v);
                 }
 
                 @Override
                 public Float get(RecentsView recentsView) {
-                    return recentsView.mTaskViewsPrimaryTranslation;
+                    return recentsView.mTaskViewsPrimarySplitTranslation;
+                }
+            };
+
+    public static final FloatProperty<RecentsView> TASK_SECONDARY_SPLIT_TRANSLATION =
+            new FloatProperty<RecentsView>("taskSecondarySplitTranslation") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setTaskViewsSecondarySplitTranslation(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mTaskViewsSecondarySplitTranslation;
                 }
             };
 
@@ -279,7 +292,6 @@
                     view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
                     view.updatePageOffsets();
                     view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
-                    view.setTaskViewsPrimaryTranslation(view.mTaskViewsPrimaryTranslation);
                 }
 
                 @Override
@@ -363,7 +375,8 @@
 
     private float mAdjacentPageOffset = 0;
     protected float mTaskViewsSecondaryTranslation = 0;
-    protected float mTaskViewsPrimaryTranslation = 0;
+    protected float mTaskViewsPrimarySplitTranslation = 0;
+    protected float mTaskViewsSecondarySplitTranslation = 0;
     // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
     private float mGridProgress = 0;
     private final IntSet mTopRowIdSet = new IntSet();
@@ -1631,7 +1644,16 @@
     /**
      * Called when a gesture from an app has finished, and an end target has been determined.
      */
-    public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
+    public void onPrepareGestureEndAnimation(
+            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+        if (mSizeStrategy.stateFromGestureEndTarget(endTarget)
+                .displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) {
+            if (animatorSet == null) {
+                setGridProgress(1);
+            } else {
+                animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
+            }
+        }
         mCurrentGestureEndTarget = endTarget;
         if (endTarget == GestureState.GestureEndTarget.NEW_TASK
                 || endTarget == GestureState.GestureEndTarget.LAST_TASK) {
@@ -1839,15 +1861,28 @@
         final int boxLength = Math.max(mLastComputedGridTaskSize.width(),
                 mLastComputedGridTaskSize.height());
         int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
-        float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
-        float taskGridVerticalDiff = mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
+
+        /*
+         * taskGridVerticalDiff is used to position the top of a task in the top row of the grid
+         * heightOffset is the vertical space one grid task takes + space between top and
+         *   bottom row
+         * Summed together they provide the top position for bottom row of grid tasks
+         */
+        final float taskGridVerticalDiff =
+                mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
+        final float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
 
         int topRowWidth = 0;
         int bottomRowWidth = 0;
         float topAccumulatedTranslationX = 0;
         float bottomAccumulatedTranslationX = 0;
+
+        // Contains whether the child index is in top or bottom of grid (for non-focused task)
+        // Different from mTopRowIdSet, which contains the taskId of what task is in top row
         IntSet topSet = new IntSet();
         IntSet bottomSet = new IntSet();
+
+        // Horizontal grid translation for each task
         float[] gridTranslations = new float[taskCount];
 
         int focusedTaskIndex = Integer.MAX_VALUE;
@@ -1901,7 +1936,6 @@
                 boolean isTopRow = isTaskDismissal ? mTopRowIdSet.contains(taskId)
                         : topRowWidth <= bottomRowWidth;
                 if (isTopRow) {
-                    gridTranslations[i] += topAccumulatedTranslationX;
                     topRowWidth += taskWidthAndSpacing;
                     topSet.add(i);
                     mTopRowIdSet.add(taskId);
@@ -1917,11 +1951,10 @@
                         widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
                     }
 
-                    float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
-                    gridTranslations[i] += gridTranslationX;
-                    topAccumulatedTranslationX += gridTranslationX;
+                    float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
+                    gridTranslations[i] += topAccumulatedTranslationX + currentTaskTranslationX;
+                    topAccumulatedTranslationX += currentTaskTranslationX;
                 } else {
-                    gridTranslations[i] += bottomAccumulatedTranslationX;
                     bottomRowWidth += taskWidthAndSpacing;
                     bottomSet.add(i);
 
@@ -1937,9 +1970,9 @@
                         widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
                     }
 
-                    float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
-                    gridTranslations[i] += gridTranslationX;
-                    bottomAccumulatedTranslationX += gridTranslationX;
+                    float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
+                    gridTranslations[i] += bottomAccumulatedTranslationX + currentTaskTranslationX;
+                    bottomAccumulatedTranslationX += currentTaskTranslationX;
                 }
                 if (taskView == snappedTaskView) {
                     snappedTaskRowWidth = isTopRow ? topRowWidth : bottomRowWidth;
@@ -2130,18 +2163,43 @@
         // Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
         // alpha is set to 0 so that it can be recycled in the view pool properly
         anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
-        FloatProperty<TaskView> secondaryViewTranslate =
-                taskView.getSecondaryDissmissTranslationProperty();
-        int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
-        int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
+        SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
 
         ResourceProvider rp = DynamicResource.provider(mActivity);
         SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START)
                 .setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio))
                 .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness));
+        FloatProperty<TaskView> dismissingTaskViewTranslate =
+                taskView.getSecondaryDissmissTranslationProperty();;
+        // TODO(b/186800707) translate entire grid size distance
+        int translateDistance = mOrientationHandler.getSecondaryDimension(taskView);
+        int positiveNegativeFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
+        if (splitController.isSplitSelectActive()) {
+            // Have the task translate towards whatever side was just pinned
+            int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitController
+                    .getActiveSplitPositionOption(), mActivity.getDeviceProfile());
+            switch (dir) {
+                case PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE:
+                    dismissingTaskViewTranslate = taskView
+                            .getSecondaryDissmissTranslationProperty();
+                    positiveNegativeFactor = -1;
+                    break;
 
-        anim.add(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
-                verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp);
+                case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE:
+                    dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
+                    positiveNegativeFactor = 1;
+                    break;
+
+                case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_NEGATIVE:
+                    dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
+                    positiveNegativeFactor = -1;
+                    break;
+                default:
+                    throw new IllegalStateException("Invalid split task translation: " + dir);
+            }
+        }
+        anim.add(ObjectAnimator.ofFloat(taskView, dismissingTaskViewTranslate,
+                positiveNegativeFactor * translateDistance).setDuration(duration), LINEAR, sp);
 
         if (LIVE_TILE.get() && taskView.isRunningTask()) {
             anim.addOnFrameCallback(() -> {
@@ -2206,18 +2264,6 @@
                     }
                 }
 
-                // Additional offset for fake landscape, if the pinning happens to the right or
-                // left, we need to scroll all the tasks away from the direction of the splaceholder
-                // view
-                if (isSplitSelectionActive()) {
-                    int splitPosition = getSplitPlaceholder().getSplitController()
-                            .getActiveSplitPositionOption().mStagePosition;
-                    int direction = mOrientationHandler
-                            .getSplitTranslationDirectionFactor(splitPosition);
-                    int splitOffset = mOrientationHandler.getSplitAnimationTranslation(
-                            mSplitPlaceholderView.getHeight(), mActivity.getDeviceProfile());
-                    offset += direction * splitOffset;
-                }
                 int scrollDiff = newScroll[i] - oldScroll[i] + offset;
                 if (scrollDiff != 0) {
                     FloatProperty translationProperty = child instanceof TaskView
@@ -2348,7 +2394,7 @@
         return true;
     }
 
-    protected void runDismissAnimation(PendingAnimation pendingAnim) {
+    private void runDismissAnimation(PendingAnimation pendingAnim) {
         AnimatorPlaybackController controller = pendingAnim.createPlaybackController();
         controller.dispatchOnStart();
         controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
@@ -2760,13 +2806,20 @@
         mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
     }
 
-    protected void setTaskViewsPrimaryTranslation(float translation) {
-        mTaskViewsPrimaryTranslation = translation;
+    protected void setTaskViewsPrimarySplitTranslation(float translation) {
+        mTaskViewsPrimarySplitTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView task = getTaskViewAt(i);
-            task.getPrimaryDismissTranslationProperty().set(task, translation / getScaleY());
+            task.getPrimarySplitTranslationProperty().set(task, translation);
         }
-        mLiveTileTaskViewSimulator.recentsViewPrimaryTranslation.value = translation;
+    }
+
+    protected void setTaskViewsSecondarySplitTranslation(float translation) {
+        mTaskViewsSecondarySplitTranslation = translation;
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView task = getTaskViewAt(i);
+            task.getSecondarySplitTranslationProperty().set(task, translation);
+        }
     }
 
     /**
@@ -2789,8 +2842,9 @@
     public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) {
         mSplitHiddenTaskView = taskView;
         SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
-        splitController.setInitialTaskSelect(taskView,
-                splitPositionOption);
+        Rect initialBounds = new Rect(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
+                taskView.getBottom());
+        splitController.setInitialTaskSelect(taskView, splitPositionOption, initialBounds);
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
         mSplitPlaceholderView.setLayoutParams(
                 splitController.getLayoutParamsForActivePosition(getResources(),
@@ -2810,7 +2864,10 @@
     }
 
     public PendingAnimation cancelSplitSelect(boolean animate) {
-        mSplitPlaceholderView.getSplitController().resetState();
+        SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
+        SplitPositionOption splitOption = splitController.getActiveSplitPositionOption();
+        Rect initialBounds = splitController.getInitialBounds();
+        splitController.resetState();
         int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
         PendingAnimation pendingAnim = new PendingAnimation(duration);
         if (!animate) {
@@ -2825,8 +2882,6 @@
         getPageScrolls(oldScroll, false,
                 view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView);
 
-        // x is correct, y is before tasks move up
-        int[] locationOnScreen = mSplitHiddenTaskView.getLocationOnScreen();
         int[] newScroll = new int[getChildCount()];
         getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC);
 
@@ -2834,20 +2889,42 @@
         for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) {
             View child = getChildAt(i);
             if (child == mSplitHiddenTaskView) {
+                TaskView taskView = (TaskView) child;
 
-                int left = newScroll[i] + getPaddingStart();
-                int topMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
-                int top = -mSplitHiddenTaskView.getHeight() - locationOnScreen[1];
-                mSplitHiddenTaskView.layout(left, top,
-                        left + mSplitHiddenTaskView.getWidth(),
-                        top + mSplitHiddenTaskView.getHeight());
-                pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, TRANSLATION_Y,
-                        -top + mSplitPlaceholderView.getHeight() - topMargin));
+                int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitOption,
+                        mActivity.getDeviceProfile());
+                FloatProperty<TaskView> dismissingTaskViewTranslate;
+                Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(),
+                        taskView.getRight(), taskView.getBottom());
+                int distanceDelta = 0;
+                if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE) {
+                    dismissingTaskViewTranslate = taskView
+                            .getSecondaryDissmissTranslationProperty();
+                    distanceDelta = initialBounds.top - hiddenBounds.top;
+                    taskView.layout(initialBounds.left, hiddenBounds.top, initialBounds.right,
+                            hiddenBounds.bottom);
+                } else {
+                    dismissingTaskViewTranslate = taskView
+                            .getPrimaryDismissTranslationProperty();
+                    distanceDelta = initialBounds.left - hiddenBounds.left;
+                    taskView.layout(hiddenBounds.left, initialBounds.top, hiddenBounds.right,
+                            initialBounds.bottom);
+                    if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE) {
+                        distanceDelta *= -1;
+                    }
+                }
+                pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView,
+                        dismissingTaskViewTranslate,
+                        distanceDelta));
                 pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1));
             } else {
                 // If insertion is on last index (furthest from clear all), we directly add the view
                 // else we translate all views to the right of insertion index further right,
                 // ignore views to left
+                if (showAsGrid()) {
+                    // TODO(b/186800707) handle more elegantly for grid
+                    continue;
+                }
                 int scrollDiff = newScroll[i] - oldScroll[i];
                 if (scrollDiff != 0) {
                     FloatProperty translationProperty = child instanceof TaskView
@@ -2873,6 +2950,12 @@
         pendingAnim.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
+                // TODO(b/186800707) Figure out how to undo for grid view
+                //  Need to handle cases where dismissed task is
+                //  * Top Row
+                //  * Bottom Row
+                //  * Focused Task
+                updateGridProperties();
                 resetFromSplitSelectionState();
             }
         });
@@ -2882,13 +2965,16 @@
 
     private void resetFromSplitSelectionState() {
         mSplitHiddenTaskView.setTranslationY(0);
-        int pageToSnapTo = mCurrentPage;
-        if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
-            pageToSnapTo += 1;
-        } else {
-            pageToSnapTo = mSplitHiddenTaskViewIndex;
+        if (!showAsGrid()) {
+            // TODO(b/186800707)
+            int pageToSnapTo = mCurrentPage;
+            if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
+                pageToSnapTo += 1;
+            } else {
+                pageToSnapTo = mSplitHiddenTaskViewIndex;
+            }
+            snapToPageImmediately(pageToSnapTo);
         }
-        snapToPageImmediately(pageToSnapTo);
         onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
         resetTaskVisuals();
         mSplitHiddenTaskView = null;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 99a8049..1477933 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -172,6 +172,32 @@
                 }
             };
 
+    private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_X =
+            new FloatProperty<TaskView>("splitSelectTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setSplitSelectTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mSplitSelectTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_Y =
+            new FloatProperty<TaskView>("splitSelectTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setSplitSelectTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mSplitSelectTranslationY;
+                }
+            };
+
     private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
             new FloatProperty<TaskView>("dismissTranslationX") {
                 @Override
@@ -345,6 +371,9 @@
     // The following grid translations scales with mGridProgress.
     private float mGridTranslationX;
     private float mGridTranslationY;
+    // Used when in SplitScreenSelectState
+    private float mSplitSelectTranslationY;
+    private float mSplitSelectTranslationX;
 
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
@@ -826,8 +855,10 @@
     protected void resetViewTransforms() {
         // fullscreenTranslation and accumulatedTranslation should not be reset, as
         // resetViewTransforms is called during Quickswitch scrolling.
-        mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX = 0f;
-        mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f;
+        mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX =
+                mSplitSelectTranslationX = 0f;
+        mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY =
+                mSplitSelectTranslationY = 0f;
         applyTranslationX();
         applyTranslationY();
         setTranslationZ(0);
@@ -957,6 +988,15 @@
         setScaleY(scale);
     }
 
+    private void setSplitSelectTranslationX(float x) {
+        mSplitSelectTranslationX = x;
+        applyTranslationX();
+    }
+
+    private void setSplitSelectTranslationY(float y) {
+        mSplitSelectTranslationY = y;
+        applyTranslationY();
+    }
     private void setDismissTranslationX(float x) {
         mDismissTranslationX = x;
         applyTranslationX();
@@ -1057,12 +1097,12 @@
 
     private void applyTranslationX() {
         setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
-                + getPersistentTranslationX());
+                + mSplitSelectTranslationX + getPersistentTranslationX());
     }
 
     private void applyTranslationY() {
         setTranslationY(mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
-                + getPersistentTranslationY());
+                + mSplitSelectTranslationY + getPersistentTranslationY());
     }
 
     /**
@@ -1086,6 +1126,16 @@
                 + getGridTrans(mGridTranslationY);
     }
 
+    public FloatProperty<TaskView> getPrimarySplitTranslationProperty() {
+        return getPagedOrientationHandler().getPrimaryValue(
+                SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getSecondarySplitTranslationProperty() {
+        return getPagedOrientationHandler().getSecondaryValue(
+                SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
+    }
+
     public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() {
         return getPagedOrientationHandler().getPrimaryValue(
                 DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 20b4631..c1f3ac5 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -57,6 +57,22 @@
 
     private final View[] mDragHandles = new View[HANDLE_COUNT];
     private final List<Rect> mSystemGestureExclusionRects = new ArrayList<>(HANDLE_COUNT);
+    private final OnAttachStateChangeListener mWidgetViewAttachStateChangeListener =
+            new OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View view) {
+                    // Do nothing
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View view) {
+                    // When the app widget view is detached, we should close the resize frame.
+                    // An example is when the dragging starts, the widget view is detached from
+                    // CellLayout and then reattached to DragLayout.
+                    close(false);
+                }
+            };
+
 
     private LauncherAppWidgetHostView mWidgetView;
     private CellLayout mCellLayout;
@@ -177,7 +193,11 @@
     private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
             DragLayer dragLayer) {
         mCellLayout = cellLayout;
+        if (mWidgetView != null) {
+            mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
+        }
         mWidgetView = widgetView;
+        mWidgetView.addOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
         LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
                 widgetView.getAppWidgetInfo();
         mResizeMode = info.resizeMode;
@@ -628,6 +648,9 @@
     @Override
     protected void handleClose(boolean animate) {
         mDragLayer.removeView(this);
+        if (mWidgetView != null) {
+            mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index a5852ba..dbdfb2b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.keyboard.KeyboardDragAndDropView;
 import com.android.launcher3.model.data.AppInfo;
@@ -290,7 +291,12 @@
             return actions;
         }
 
-        CellLayout layout = (CellLayout) host.getParent().getParent();
+        CellLayout layout;
+        if (host.getParent() instanceof DragView) {
+            layout = (CellLayout) ((DragView) host.getParent()).getContentViewParent().getParent();
+        } else {
+            layout = (CellLayout) host.getParent().getParent();
+        }
         if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
             if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
                     layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 2fd5efc..44e55e1 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -160,6 +160,19 @@
     }
 
     @Override
+    public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+            DeviceProfile dp) {
+        // Don't use device profile here because we know we're in fake landscape, only split option
+        // available is top/left
+        if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+            // Top (visually left) side
+            return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
+        }
+        throw new IllegalStateException("Invalid split stage position: " +
+                splitPosition.mStagePosition);
+    }
+
+    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollY();
     }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 6c2f17e..d8e5a48 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -42,6 +42,10 @@
  */
 public interface PagedOrientationHandler {
 
+    int SPLIT_TRANSLATE_PRIMARY_POSITIVE = 0;
+    int SPLIT_TRANSLATE_PRIMARY_NEGATIVE = 1;
+    int SPLIT_TRANSLATE_SECONDARY_NEGATIVE = 2;
+
     PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
     PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
     PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
@@ -71,6 +75,13 @@
     int getSecondaryDimension(View view);
     FloatProperty<View> getPrimaryViewTranslate();
     FloatProperty<View> getSecondaryViewTranslate();
+
+    /**
+     * @param splitPosition The position where the view to be split will go
+     * @return {@link #SPLIT_TRANSLATE_*} constants to indicate which direction the
+     * dismissal should happen
+     */
+    int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition, DeviceProfile dp);
     int getPrimaryScroll(View view);
     float getPrimaryScale(View view);
     int getChildStart(View view);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index f6a6448..d3d77fd 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -156,6 +156,25 @@
     }
 
     @Override
+    public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+            DeviceProfile dp) {
+        if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+            if (dp.isLandscape) {
+                // Left side
+                return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
+            } else {
+                // Top side
+                return SPLIT_TRANSLATE_SECONDARY_NEGATIVE;
+            }
+        } else if (splitPosition.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+            // We don't have a bottom option, so should be right
+            return SPLIT_TRANSLATE_PRIMARY_POSITIVE;
+        }
+        throw new IllegalStateException("Invalid split stage position: " +
+                splitPosition.mStagePosition);
+    }
+
+    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollX();
     }
@@ -277,7 +296,7 @@
         } else {
             // Phone Portrait, LargeScreen Landscape/Portrait
             viewGroup.setOrientation(LinearLayout.HORIZONTAL);
-            lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
+            lp.width = LinearLayout.LayoutParams.MATCH_PARENT;
         }
 
         lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index be14e01..f5e1234 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -48,7 +48,13 @@
             WindowInsets rootInsets = launcher.getRootView().getRootWindowInsets();
             boolean isImeShown = rootInsets != null && rootInsets.isVisible(
                     WindowInsets.Type.ime());
-            if (!isImeShown) return;
+            if (isImeShown) {
+                // this call is already asynchronous
+                launcher.getAppsView().getWindowInsetsController().hide(
+                        WindowInsets.Type.ime()
+                );
+            }
+            return;
         }
 
         Message.obtain(HANDLER.get(launcher), MSG_HIDE_KEYBOARD, token).sendToTarget();