Fix black flash when splitting task

- Draw the thumbnail view and align with the thumbnail bounds instead of
  the whole task bounds with the icon
- Defer animating the task list until after the animation completes

Bug: 73118672
Test: Enter split screen
Change-Id: Ie10c079cb22ae82f3c5974296462abae335ef5a8
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
index c8b54ad..8b73809 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
@@ -198,8 +198,8 @@
             }
         } else {
             if (goingUp) {
-                mPendingAnimation = mRecentsView
-                        .createTaskDismissAnimation(mTaskBeingDragged, maxDuration);
+                mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
+                        true /* animateTaskView */, true /* removeTask */, maxDuration);
                 mCurrentAnimation = AnimatorPlaybackController
                         .wrap(mPendingAnimation.anim, maxDuration);
                 mEndDisplacement = -mTaskBeingDragged.getHeight();
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index 2022595..08be0c8 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -31,12 +33,15 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
@@ -57,6 +62,7 @@
 public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
 
     private static final String TAG = "TaskSystemShortcut";
+    private static final int DISMISS_TASK_DURATION = 300;
 
     protected T mSystemShortcut;
 
@@ -99,10 +105,13 @@
         }
     }
 
-    public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener {
+    public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener,
+            DeviceProfile.OnDeviceProfileChangeListener, View.OnLayoutChangeListener {
 
         private Handler mHandler;
+        private RecentsView mRecentsView;
         private TaskView mTaskView;
+        private BaseDraggingActivity mActivity;
 
         public SplitScreen() {
             super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
@@ -116,15 +125,19 @@
                 return null;
             }
             final Task task  = taskView.getTask();
+            final int taskId = task.key.id;
             if (!task.isDockable) {
                 return null;
             }
+            mActivity = activity;
+            mRecentsView = activity.getOverviewPanel();
             mTaskView = taskView;
+            final TaskThumbnailView thumbnailView = taskView.getThumbnail();
             return (v -> {
                 AbstractFloatingView.closeOpenViews(activity, true,
                         AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
 
-                if (ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key.id,
+                if (ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
                         ActivityOptionsCompat.makeSplitScreenOptions(true))) {
                     ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
                     try {
@@ -134,26 +147,35 @@
                         return;
                     }
 
+                    // Add a device profile change listener to kick off animating the side tasks
+                    // once we enter multiwindow mode and relayout
+                    activity.addOnDeviceProfileChangeListener(this);
+
                     final Runnable animStartedListener = () -> {
+                        // Hide the task view and wait for the window to be resized
+                        // TODO: Consider animating in launcher and do an in-place start activity
+                        //       afterwards
+                        mRecentsView.addIgnoreResetTask(mTaskView);
+                        mTaskView.setAlpha(0f);
                         mTaskView.getViewTreeObserver().addOnPreDrawListener(SplitScreen.this);
-                        activity.<RecentsView>getOverviewPanel().removeView(taskView);
                     };
 
                     final int[] position = new int[2];
-                    taskView.getLocationOnScreen(position);
-                    final int width = (int) (taskView.getWidth() * taskView.getScaleX());
-                    final int height = (int) (taskView.getHeight() * taskView.getScaleY());
+                    thumbnailView.getLocationOnScreen(position);
+                    final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
+                    final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
                     final Rect taskBounds = new Rect(position[0], position[1],
                             position[0] + width, position[1] + height);
 
                     Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
-                            taskBounds.width(), taskBounds.height(), taskView, 1f, Color.BLACK);
+                            taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
+                            Color.BLACK);
                     AppTransitionAnimationSpecsFuture future =
                             new AppTransitionAnimationSpecsFuture(mHandler) {
                         @Override
                         public List<AppTransitionAnimationSpecCompat> composeSpecs() {
                             return Collections.singletonList(new AppTransitionAnimationSpecCompat(
-                                    task.key.id, thumbnail, taskBounds));
+                                    taskId, thumbnail, taskBounds));
                         }
                     };
                     WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
@@ -168,6 +190,31 @@
             WindowManagerWrapper.getInstance().endProlongedAnimations();
             return true;
         }
+
+        @Override
+        public void onDeviceProfileChanged(DeviceProfile dp) {
+            mActivity.removeOnDeviceProfileChangeListener(this);
+            if (dp.isMultiWindowMode) {
+                mTaskView.getRootView().addOnLayoutChangeListener(this);
+            }
+        }
+
+        @Override
+        public void onLayoutChange(View v, int l, int t, int r, int b,
+                int oldL, int oldT, int oldR, int oldB) {
+            mTaskView.getRootView().removeOnLayoutChangeListener(this);
+            mRecentsView.removeIgnoreResetTask(mTaskView);
+
+            // Start animating in the side pages once launcher has been resized
+            PendingAnimation pendingAnim = mRecentsView.createTaskDismissAnimation(mTaskView,
+                    false, false, DISMISS_TASK_DURATION);
+            AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(
+                    pendingAnim.anim, DISMISS_TASK_DURATION);
+            controller.dispatchOnStart();
+            controller.setEndAction(() -> pendingAnim.finish(true));
+            controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
+            controller.start();
+        }
     }
 
     public static class Pin extends TaskSystemShortcut {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 23e6e5b..68b9d45 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -30,6 +30,7 @@
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.graphics.Rect;
 import android.os.Build;
+import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.SparseBooleanArray;
 import android.view.LayoutInflater;
@@ -104,6 +105,9 @@
 
     private PendingAnimation mPendingAnimation;
 
+    // Keeps track of task views whose visual state should not be reset
+    private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
+
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
@@ -261,7 +265,10 @@
 
     public void resetTaskVisuals() {
         for (int i = getChildCount() - 1; i >= 0; i--) {
-            ((TaskView) getChildAt(i)).resetVisualProperties();
+            TaskView taskView = (TaskView) getChildAt(i);
+            if (!mIgnoreResetTaskViews.contains(taskView)) {
+                taskView.resetVisualProperties();
+            }
         }
 
         updateCurveProperties();
@@ -512,7 +519,16 @@
         public float linearInterpolation;
     }
 
-    public PendingAnimation createTaskDismissAnimation(TaskView taskView, long duration) {
+    public void addIgnoreResetTask(TaskView taskView) {
+        mIgnoreResetTaskViews.add(taskView);
+    }
+
+    public void removeIgnoreResetTask(TaskView taskView) {
+        mIgnoreResetTaskViews.remove(taskView);
+    }
+
+    public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
+            boolean removeTask, long duration) {
         if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
             throw new IllegalStateException("Another pending animation is still running");
         }
@@ -543,9 +559,11 @@
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
             if (child == taskView) {
-                addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
-                addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
-                        duration, LINEAR, anim);
+                if (animateTaskView) {
+                    addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
+                    addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
+                            duration, LINEAR, anim);
+                }
             } else {
                 int scrollDiff = newScroll[i] - oldScroll[i] + maxScrollDiff;
                 if (scrollDiff != 0) {
@@ -563,12 +581,16 @@
         }
 
         // Add a tiny bit of translation Z, so that it draws on top of other views
-        taskView.setTranslationZ(0.1f);
+        if (animateTaskView) {
+            taskView.setTranslationZ(0.1f);
+        }
 
         mPendingAnimation = pendingAnimation;
         mPendingAnimation.addEndListener((isSuccess) -> {
            if (isSuccess) {
-               ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
+               if (removeTask) {
+                   ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
+               }
                removeView(taskView);
                if (getChildCount() == 0) {
                    onAllTasksRemoved();
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 77a430b..02d70c4 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -98,9 +98,12 @@
         mDPChangeListeners.add(listener);
     }
 
+    public void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
+        mDPChangeListeners.remove(listener);
+    }
+
     protected void dispatchDeviceProfileChanged() {
-        int count = mDPChangeListeners.size();
-        for (int i = 0; i < count; i++) {
+        for (int i = mDPChangeListeners.size() - 1; i >= 0; i--) {
             mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile);
         }
     }