Merge "Redraw live tile in updatePageOffsets()" into sc-dev
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 827eb7d..62ab95a 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -172,6 +172,8 @@
     private final float mClosingWindowTransY;
     private final float mMaxShadowRadius;
 
+    private final StartingWindowListener mStartingWindowListener = new StartingWindowListener();
+
     private DeviceProfile mDeviceProfile;
 
     private RemoteAnimationProvider mRemoteAnimationProvider;
@@ -221,13 +223,9 @@
                 }
             };
 
+            mStartingWindowListener.setTransitionManager(this);
             SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(
-                    new IStartingWindowListener.Stub() {
-                        @Override
-                        public void onTaskLaunching(int taskId, int supportedType) {
-                            mTypeForTaskId.put(taskId, supportedType);
-                        }
-                    });
+                    mStartingWindowListener);
         }
     }
 
@@ -566,7 +564,6 @@
         // Set the crop here so we can calculate the corner radius below.
         crop.set(left, top, right, bottom);
 
-        RectF targetBounds = new RectF(windowTargetBounds);
         RectF floatingIconBounds = new RectF();
         RectF tmpRectF = new RectF();
         Point tmpPos = new Point();
@@ -655,12 +652,8 @@
                 tmpRectF.offset(dragLayerBounds[0], dragLayerBounds[1]);
                 tmpRectF.offset(mDx.value, mDy.value);
                 Utilities.scaleRectFAboutCenter(tmpRectF, mIconScaleToFitScreen.value);
-                float windowTransX0 = tmpRectF.left - offsetX;
-                float windowTransY0 = tmpRectF.top - offsetY;
-                if (hasSplashScreen) {
-                    windowTransX0 -= crop.left * scale;
-                    windowTransY0 -= crop.top * scale;
-                }
+                float windowTransX0 = tmpRectF.left - offsetX - crop.left * scale;
+                float windowTransY0 = tmpRectF.top - offsetY - crop.top * scale;
 
                 // Calculate the icon position.
                 floatingIconBounds.set(launcherIconBounds);
@@ -819,6 +812,7 @@
     public void onActivityDestroyed() {
         unregisterRemoteAnimations();
         unregisterRemoteTransitions();
+        mStartingWindowListener.setTransitionManager(null);
         SystemUiProxy.INSTANCE.getNoCreate().setStartingWindowListener(null);
     }
 
@@ -1213,25 +1207,16 @@
             alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
                     : APP_LAUNCH_ALPHA_DOWN_DURATION;
 
-            if (hasSplashScreen) {
-                iconAlphaStart = 0;
+            iconAlphaStart = hasSplashScreen ? 0 : 1f;
 
-                // TOOD: Share value from shell when available.
-                final float windowIconSize = Utilities.pxFromSp(108, r.getDisplayMetrics());
+            // TOOD: Share value from shell when available.
+            final float windowIconSize = Utilities.pxFromSp(108, r.getDisplayMetrics());
 
-                cropCenterXStart = windowTargetBounds.centerX();
-                cropCenterYStart = windowTargetBounds.centerY();
+            cropCenterXStart = windowTargetBounds.centerX();
+            cropCenterYStart = windowTargetBounds.centerY();
 
-                cropWidthStart = (int) windowIconSize;
-                cropHeightStart = (int) windowIconSize;
-            } else {
-                iconAlphaStart = 1;
-
-                cropWidthStart = cropHeightStart =
-                        Math.min(windowTargetBounds.width(), windowTargetBounds.height());
-                cropCenterXStart = cropCenterYStart =
-                        Math.min(windowTargetBounds.centerX(), windowTargetBounds.centerY());
-            }
+            cropWidthStart = (int) windowIconSize;
+            cropHeightStart = (int) windowIconSize;
 
             cropWidthEnd = windowTargetBounds.width();
             cropHeightEnd = windowTargetBounds.height();
@@ -1240,4 +1225,17 @@
             cropCenterYEnd = windowTargetBounds.centerY();
         }
     }
+
+    private static class StartingWindowListener extends IStartingWindowListener.Stub {
+        private QuickstepTransitionManager mTransitionManager;
+
+        public void setTransitionManager(QuickstepTransitionManager transitionManager) {
+            mTransitionManager = transitionManager;
+        }
+
+        @Override
+        public void onTaskLaunching(int taskId, int supportedType) {
+            mTransitionManager.mTypeForTaskId.put(taskId, supportedType);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
index 351adf4..c527be3 100644
--- a/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
+++ b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
@@ -46,7 +46,8 @@
     }
 
     public boolean get() {
-        return mBasePredicate.get() && mSupported && mObserver.isHomeAndOverviewSame();
+        return mBasePredicate.get() && mSupported && mObserver != null
+                && mObserver.isHomeAndOverviewSame();
     }
 
     public void initialize(Context context) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 5761dfb..d59d120 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -142,7 +142,6 @@
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.ViewUtils;
 import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.SplitScreenBounds;
 import com.android.quickstep.util.SplitSelectStateController;
@@ -328,6 +327,8 @@
     private float mFullscreenScale;
 
     private static final int DISMISS_TASK_DURATION = 300;
+    private static final int DISMISS_TASK_TRANSLATION_DURATION = 200;
+    private static final int ADDITIONAL_DISMISS_TASK_TRANSLATION_DURATION = 75;
     private static final int ADDITION_TASK_DURATION = 200;
     // The threshold at which we update the SystemUI flags when animating from the task into the app
     public static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.85f;
@@ -365,15 +366,13 @@
     protected float mTaskViewsPrimaryTranslation = 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();
 
     // The GestureEndTarget that is still in progress.
     protected GestureState.GestureEndTarget mCurrentGestureEndTarget;
 
-    IntSet mTopIdSet = new IntSet();
-
     private int mOverScrollShift = 0;
 
-
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
      */
@@ -825,18 +824,16 @@
             ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
             appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
             appAnimator.setInterpolator(ACCEL_DEACCEL);
-            appAnimator.addUpdateListener(new MultiValueUpdateListener() {
-                @Override
-                public void onUpdate(float percent) {
-                    SurfaceParams.Builder builder = new SurfaceParams.Builder(
-                            apps[apps.length - 1].leash);
-                    Matrix matrix = new Matrix();
-                    matrix.postScale(percent, percent);
-                    matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
-                            mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
-                    builder.withAlpha(percent).withMatrix(matrix);
-                    surfaceApplier.scheduleApply(builder.build());
-                }
+            appAnimator.addUpdateListener(valueAnimator -> {
+                float percent = valueAnimator.getAnimatedFraction();
+                SurfaceParams.Builder builder = new SurfaceParams.Builder(
+                        apps[apps.length - 1].leash);
+                Matrix matrix = new Matrix();
+                matrix.postScale(percent, percent);
+                matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
+                        mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
+                builder.withAlpha(percent).withMatrix(matrix);
+                surfaceApplier.scheduleApply(builder.build());
             });
             anim.play(appAnimator);
             anim.addListener(new AnimatorListenerAdapter() {
@@ -1299,7 +1296,7 @@
         }
         mClearAllButton.setFullscreenTranslationPrimary(accumulatedTranslationX);
 
-        updateGridProperties(/*isTaskDismissal=*/false);
+        updateGridProperties();
     }
 
     public void getTaskSize(Rect outRect) {
@@ -1659,7 +1656,7 @@
             // When switching to tasks in quick switch, ensures the snapped page's scroll maintain
             // invariant between quick switch and overview grid, to ensure a smooth animation
             // transition.
-            updateGridProperties(/*isTaskDismissal=*/false);
+            updateGridProperties();
         }
     }
 
@@ -1832,13 +1829,26 @@
         }
     }
 
+    /** Updates TaskView and ClearAllButtion scaling and translation required to turn into grid
+     * layout.
+     * This method is used when no task dismissal has occurred.
+     */
+    private void updateGridProperties() {
+        updateGridProperties(null, -1);
+    }
+
     /**
      * Updates TaskView and ClearAllButton scaling and translation required to turn into grid
      * layout.
      * This method only calculates the potential position and depends on {@link #setGridProgress} to
-     * apply the actual scaling and translation.
+     * apply the actual scaling and translation. This adds task translation animations in the case
+     * of task dismissals: e.g. when dismissedTask is not null.
+     *
+     * @param dismissedTask the TaskView dismissed, possibly null
+     * @param dismissedIndex the index at which the dismissedTask was prior to dismissal, if no
+     *                       dismissal occurred, this is unused
      */
-    private void updateGridProperties(boolean isTaskDismissal) {
+    private void updateGridProperties(TaskView dismissedTask, int dismissedIndex) {
         int taskCount = getTaskViewCount();
         if (taskCount == 0) {
             return;
@@ -1865,8 +1875,12 @@
         int snappedPage = getNextPage();
         TaskView snappedTaskView = getTaskViewAtByAbsoluteIndex(snappedPage);
 
+        boolean isTaskDismissal = dismissedTask != null;
+        float dismissedTaskWidth =
+                isTaskDismissal ? dismissedTask.getLayoutParams().width + mPageSpacing : 0;
+
         if (!isTaskDismissal) {
-            mTopIdSet.clear();
+            mTopRowIdSet.clear();
         }
         for (int i = 0; i < taskCount; i++) {
             TaskView taskView = getTaskViewAt(i);
@@ -1901,13 +1915,14 @@
                     // calculate the distance focused task need to shift.
                     focusedTaskShift += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
                 }
-                boolean isTopRow = isTaskDismissal ? mTopIdSet.contains(taskView.getTask().key.id)
+                int taskId = taskView.getTask().key.id;
+                boolean isTopRow = isTaskDismissal ? mTopRowIdSet.contains(taskId)
                         : topRowWidth <= bottomRowWidth;
                 if (isTopRow) {
                     gridTranslations[i] += topAccumulatedTranslationX;
                     topRowWidth += taskWidthAndSpacing;
                     topSet.add(i);
-                    mTopIdSet.add(taskView.getTask().key.id);
+                    mTopRowIdSet.add(taskId);
 
                     taskView.setGridTranslationY(taskGridVerticalDiff);
 
@@ -1961,17 +1976,55 @@
             snappedTaskGridTranslationX = gridTranslations[snappedPage - mTaskViewStartIndex];
         }
 
-
+        // Animate task dismissTranslationX for tasks with index >= dismissed index and in the
+        // same row as the dismissed index, or if the dismissed task was the focused task. Offset
+        // successive task dismissal durations for a staggered effect.
+        ArrayList<Animator> gridTranslationAnimators = new ArrayList<>();
+        boolean isFocusedTaskDismissed =
+                isTaskDismissal && dismissedTask.getTask().key.id == mFocusedTaskId;
         for (int i = 0; i < taskCount; i++) {
             TaskView taskView = getTaskViewAt(i);
+            if (isFocusedTaskDismissed || (i >= dismissedIndex && isSameGridRow(dismissedTask,
+                    taskView))) {
+                Animator taskDismissAnimator = ObjectAnimator.ofFloat(taskView,
+                        taskView.getPrimaryDismissTranslationProperty(),
+                        mIsRtl ? -dismissedTaskWidth : dismissedTaskWidth, 0f);
+                int additionalTranslationDuration =
+                        i >= dismissedIndex ? (ADDITIONAL_DISMISS_TASK_TRANSLATION_DURATION * (
+                                (i - dismissedIndex) / 2)) : 0;
+                taskDismissAnimator.setDuration(
+                        DISMISS_TASK_TRANSLATION_DURATION + additionalTranslationDuration);
+                gridTranslationAnimators.add(taskDismissAnimator);
+            }
             taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX);
             taskView.setNonFullscreenTranslationX(snappedTaskFullscreenScrollAdjustment);
         }
+        AnimatorSet gridTranslationAnimatorSet = new AnimatorSet();
+        gridTranslationAnimatorSet.playTogether(gridTranslationAnimators);
+        gridTranslationAnimatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            // Allow the actions view to display again once in focus mode
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                if (getFocusedTaskView() == null) {
+                    mActionsView.getScrollAlpha().setValue(1);
+                }
+            }
 
-        // Use the accumulated translation of the longer row.
-        float clearAllAccumulatedTranslation = mIsRtl ? Math.max(topAccumulatedTranslationX,
-                bottomAccumulatedTranslationX) : Math.min(topAccumulatedTranslationX,
-                bottomAccumulatedTranslationX);
+            @Override
+            // Hide the actions view if not in focus mode
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                if (getFocusedTaskView() == null) {
+                    mActionsView.getScrollAlpha().setValue(0);
+                }
+            }
+        });
+        gridTranslationAnimatorSet.start();
+
+        // Use the accumulated translation of the row containing the last task.
+        float clearAllAccumulatedTranslation = topSet.contains(taskCount - 1)
+                ? topAccumulatedTranslationX : bottomAccumulatedTranslationX;
 
         // If the last task is on the shorter row, ClearAllButton will embed into the shorter row
         // which is not what we want. Compensate the width difference of the 2 rows in that case.
@@ -1989,13 +2042,14 @@
                 mIsRtl ? -shorterRowCompensation : shorterRowCompensation;
 
         // If the total width is shorter than one grid's width, move ClearAllButton further away
-        // accordingly.
+        // accordingly. Update longRowWidth if ClearAllButton has been moved.
         float clearAllShortTotalCompensation = 0;
         int longRowWidth = Math.max(topRowWidth, bottomRowWidth);
         if (longRowWidth < mLastComputedGridSize.width()) {
             float shortTotalCompensation = mLastComputedGridSize.width() - longRowWidth;
             clearAllShortTotalCompensation =
                     mIsRtl ? -shortTotalCompensation : shortTotalCompensation;
+            longRowWidth = mLastComputedGridSize.width();
         }
 
         float clearAllTotalTranslationX =
@@ -2028,6 +2082,19 @@
         setGridProgress(mGridProgress);
     }
 
+    private boolean isSameGridRow(TaskView taskView1, TaskView taskView2) {
+        if (taskView1 == null || taskView2 == null) {
+            return false;
+        }
+        int taskId1 = taskView1.getTask().key.id;
+        int taskId2 = taskView2.getTask().key.id;
+        if (taskId1 == mFocusedTaskId || taskId2 == mFocusedTaskId) {
+            return false;
+        }
+        return (mTopRowIdSet.contains(taskId1) && mTopRowIdSet.contains(taskId2)) || (
+                !mTopRowIdSet.contains(taskId1) && !mTopRowIdSet.contains(taskId2));
+    }
+
     /**
      * Moves TaskView and ClearAllButton between carousel and 2 row grid.
      *
@@ -2238,8 +2305,12 @@
                     }
 
                     int pageToSnapTo = mCurrentPage;
-                    if (draggedIndex < pageToSnapTo ||
-                            pageToSnapTo == (getTaskViewCount() - 1)) {
+                    // Snap to start if focused task was dismissed, as after quick switch it could
+                    // be at any page but the focused task always displays at the start.
+                    if (taskView.getTask().key.id == mFocusedTaskId) {
+                        pageToSnapTo = mTaskViewStartIndex;
+                    } else if (draggedIndex < pageToSnapTo || pageToSnapTo == (getTaskViewCount()
+                            - 1)) {
                         pageToSnapTo -= 1;
                     }
                     removeViewInLayout(taskView);
@@ -2250,7 +2321,7 @@
                     } else {
                         snapToPageImmediately(pageToSnapTo);
                         // Grid got messed up, reapply.
-                        updateGridProperties(/*isTaskDismissal=*/true);
+                        updateGridProperties(taskView, draggedIndex - mTaskViewStartIndex);
                         if (showAsGrid() && getFocusedTaskView() == null) {
                             animateActionsViewOut();
                         }
@@ -2259,7 +2330,9 @@
                     // immediately available.
                     onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
                 }
-                resetTaskVisuals();
+                if (!showAsGrid()) {
+                    resetTaskVisuals();
+                }
                 onDismissAnimationEnds();
                 mPendingAnimation = null;
             }