Merge "Cancel badge scale anim when setting new badge scale" into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index a1ae99e..8684c58 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -46,6 +46,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 
@@ -97,11 +98,17 @@
             }
             return false;
         }
+        RecentsView recentsView = mLauncher.getOverviewPanel();
         if (mLauncher.isInState(ALL_APPS)) {
             // In all-apps only listen if the container cannot scroll itself
             if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
                 return false;
             }
+        } else if (mLauncher.isInState(OVERVIEW) && recentsView.getChildCount() > 0) {
+            // Allow swiping up in the gap between the hotseat and overview.
+            if (ev.getY() < recentsView.getChildAt(0).getBottom()) {
+                return false;
+            }
         } else {
             // For all other states, only listen if the event originated below the hotseat height
             DeviceProfile dp = mLauncher.getDeviceProfile();
@@ -197,7 +204,7 @@
             // Reset the state manager, when changing the interaction mode
             mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */);
             mPendingAnimation = recentsView.createTaskLauncherAnimation(taskView, maxAccuracy);
-            mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
+            mPendingAnimation.anim.setInterpolator(Interpolators.LINEAR);
 
             Runnable onCancelRunnable = () -> {
                 cancelPendingAnim();
@@ -206,6 +213,7 @@
             mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy,
                     onCancelRunnable);
             mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
+            totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher.getDeviceProfile());
         } else {
             mCurrentAnimation = mLauncher.getStateManager()
                     .createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState,
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 322e270..77af211 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -16,7 +16,6 @@
 package com.android.quickstep;
 
 import static android.view.View.TRANSLATION_Y;
-
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
@@ -38,6 +37,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Handler;
@@ -100,8 +100,13 @@
 
     void onTransitionCancelled(T activity, boolean activityVisible);
 
+    default int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
+            @InteractionType int interactionType, TransformedRect outRect) {
+        return getSwipeUpDestinationAndLength(dp, context, interactionType, outRect, null);
+    }
+
     int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-            @InteractionType int interactionType, TransformedRect outRect);
+            @InteractionType int interactionType, TransformedRect outRect, PointF touchTown);
 
     void onSwipeUpComplete(T activity);
 
@@ -185,7 +190,7 @@
 
         @Override
         public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-                @InteractionType int interactionType, TransformedRect outRect) {
+                @InteractionType int interactionType, TransformedRect outRect, PointF touchDown) {
             LayoutUtils.calculateLauncherTaskSize(context, dp, outRect.rect);
             if (interactionType == INTERACTION_QUICK_SCRUB) {
                 outRect.scale = FastOverviewState.getOverviewScale(dp, outRect.rect, context);
@@ -195,9 +200,12 @@
                 int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
                 return dp.hotseatBarSizePx + hotseatInset;
             } else {
-                int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
-                // Track slightly below the top of the shelf (between top and content).
-                return shelfHeight - dp.edgeMarginPx * 2;
+                int swipeLength = LayoutUtils.getShelfTrackingDistance(dp);
+                if (touchDown != null) {
+                    // We are already partway through based on where we touched the nav bar.
+                    swipeLength -= dp.heightPx - touchDown.y;
+                }
+                return swipeLength;
             }
         }
 
@@ -456,7 +464,7 @@
 
         @Override
         public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-                @InteractionType int interactionType, TransformedRect outRect) {
+                @InteractionType int interactionType, TransformedRect outRect, PointF touchDown) {
             LayoutUtils.calculateFallbackTaskSize(context, dp, outRect.rect);
             if (dp.isVerticalBarLayout()) {
                 Rect targetInsets = dp.getInsets();
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 0e811f7..c3e0568 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -444,7 +444,7 @@
             } else {
                 TraceHelper.partitionSection("RecentsController", "Received");
                 mInteractionHandler.onRecentsAnimationStart(mController, mTargets,
-                        mHomeContentInsets, mMinimizedHomeBounds);
+                        mHomeContentInsets, mMinimizedHomeBounds, mDownPos);
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 66655bd..18fbfbb 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -36,6 +36,7 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
@@ -175,6 +176,7 @@
     protected boolean mIsGoingToHome;
     private DeviceProfile mDp;
     private int mTransitionDragLength;
+    private PointF mTouchDown;
 
     // Shift in the range of [0, 1].
     // 0 => preview snapShot is completely visible, and hotseat is completely translated down
@@ -340,12 +342,12 @@
         }
     }
 
-    private void initTransitionEndpoints(DeviceProfile dp) {
+    private void initTransitionEndpoints(DeviceProfile dp, PointF touchDown) {
         mDp = dp;
 
         TransformedRect tempRect = new TransformedRect();
-        mTransitionDragLength = mActivityControlHelper
-                .getSwipeUpDestinationAndLength(dp, mContext, mInteractionType, tempRect);
+        mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
+                dp, mContext, mInteractionType, tempRect, touchDown);
         mClipAnimationHelper.updateTargetRect(tempRect);
     }
 
@@ -569,7 +571,7 @@
      * Called by {@link #mLayoutListener} when launcher layout changes
      */
     public void buildAnimationController() {
-        initTransitionEndpoints(mActivity.getDeviceProfile());
+        initTransitionEndpoints(mActivity.getDeviceProfile(), mTouchDown);
         mAnimationFactory.createActivityController(mTransitionDragLength, mInteractionType);
     }
 
@@ -619,7 +621,8 @@
     }
 
     public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller,
-            RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds) {
+            RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds,
+            PointF touchDown) {
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
         final Rect overviewStackBounds;
         RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mRunningTaskId);
@@ -650,7 +653,8 @@
             mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
         }
         mClipAnimationHelper.prepareAnimation(false /* isOpening */);
-        initTransitionEndpoints(dp);
+        mTouchDown = touchDown;
+        initTransitionEndpoints(dp, mTouchDown);
 
         mRecentsAnimationWrapper.setController(controller, targets);
         setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 7715f35..253e56f 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -111,4 +111,10 @@
         outRect.set(Math.round(x), Math.round(y),
                 Math.round(x + outWidth), Math.round(y + outHeight));
     }
+
+    public static int getShelfTrackingDistance(DeviceProfile dp) {
+        int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
+        // Track slightly below the top of the shelf (between top and content).
+        return shelfHeight - dp.edgeMarginPx * 2;
+    }
 }
diff --git a/res/values/config.xml b/res/values/config.xml
index 8d31bd2..2a6b25c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -122,7 +122,7 @@
 
 <!-- Popup items -->
     <integer name="config_popupOpenCloseDuration">150</integer>
-    <integer name="config_popupArrowOpenDuration">80</integer>
+    <integer name="config_popupArrowOpenCloseDuration">40</integer>
     <integer name="config_removeNotificationViewDuration">300</integer>
 
 <!-- Accessibility actions -->
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7f99323..f0ddd53 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -353,6 +353,7 @@
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
         UiFactory.onEnterAnimationComplete(this);
+        mAllAppsController.highlightWorkTabIfNecessary();
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 6908ab4..3d15c75 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -486,8 +486,13 @@
     }
 
     public void onScrollUpEnd() {
+        highlightWorkTabIfNecessary();
+    }
+
+    void highlightWorkTabIfNecessary() {
         if (mUsingTabs) {
-            ((PersonalWorkSlidingTabStrip) findViewById(R.id.tabs)).highlightWorkTabIfNecessary();
+            ((PersonalWorkSlidingTabStrip) findViewById(R.id.tabs))
+                    .highlightWorkTabIfNecessary();
         }
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 24a8d51..e7313e8 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -249,11 +249,21 @@
         if (Float.compare(mProgress, 1f) == 0) {
             mAppsView.setVisibility(View.INVISIBLE);
             mAppsView.reset(false /* animate */);
-        } else if (Float.compare(mProgress, 0f) == 0) {
+        } else if (isAllAppsExpanded()) {
             mAppsView.setVisibility(View.VISIBLE);
             mAppsView.onScrollUpEnd();
         } else {
             mAppsView.setVisibility(View.VISIBLE);
         }
     }
+
+    private boolean isAllAppsExpanded() {
+        return Float.compare(mProgress, 0f) == 0;
+    }
+
+    public void highlightWorkTabIfNecessary() {
+        if (isAllAppsExpanded()) {
+            mAppsView.highlightWorkTabIfNecessary();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 3bf8bef..0bb5e2a 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.popup;
 
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -35,7 +37,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
-import android.view.animation.AccelerateDecelerateInterpolator;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -66,7 +67,7 @@
     protected final Launcher mLauncher;
     protected final boolean mIsRtl;
 
-    private final int mArrayOffset;
+    private final int mArrowOffset;
     private final View mArrow;
 
     protected boolean mIsLeftAligned;
@@ -99,7 +100,7 @@
         final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
         mArrow = new View(context);
         mArrow.setLayoutParams(new DragLayer.LayoutParams(arrowWidth, arrowHeight));
-        mArrayOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
+        mArrowOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
     }
 
     public ArrowPopup(Context context, AttributeSet attrs) {
@@ -187,11 +188,17 @@
             int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
             arrowPaint.setPathEffect(new CornerPathEffect(radius));
             mArrow.setBackground(arrowDrawable);
+            // Clip off the part of the arrow that is underneath the popup.
+            if (mIsAboveIcon) {
+                mArrow.setClipBounds(new Rect(0, -mArrowOffset, arrowLp.width, arrowLp.height));
+            } else {
+                mArrow.setClipBounds(new Rect(0, 0, arrowLp.width, arrowLp.height + mArrowOffset));
+            }
             mArrow.setElevation(getElevation());
         }
 
         mArrow.setPivotX(arrowLp.width / 2);
-        mArrow.setPivotY(mIsAboveIcon ? 0 : arrowLp.height);
+        mArrow.setPivotY(mIsAboveIcon ? arrowLp.height : 0);
 
         animateOpen();
     }
@@ -220,7 +227,7 @@
     protected void orientAboutObject() {
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         int width = getMeasuredWidth();
-        int extraVerticalSpace = mArrow.getLayoutParams().height + mArrayOffset
+        int extraVerticalSpace = mArrow.getLayoutParams().height + mArrowOffset
                 + getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
         int height = getMeasuredHeight() + extraVerticalSpace;
 
@@ -309,11 +316,11 @@
         if (mIsAboveIcon) {
             arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
             lp.bottomMargin = getPopupContainer().getHeight() - y - getMeasuredHeight() - insets.top;
-            arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrayOffset - insets.bottom;
+            arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrowOffset - insets.bottom;
         } else {
             arrowLp.gravity = lp.gravity = Gravity.TOP;
             lp.topMargin = y + insets.top;
-            arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrayOffset;
+            arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrowOffset;
         }
     }
 
@@ -343,7 +350,8 @@
         final AnimatorSet openAnim = new AnimatorSet();
         final Resources res = getResources();
         final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
-        final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+        final long arrowDuration = res.getInteger(R.integer.config_popupArrowOpenCloseDuration);
+        final TimeInterpolator revealInterpolator = ACCEL_DEACCEL;
 
         // Rectangular reveal.
         final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
@@ -351,16 +359,21 @@
         revealAnim.setDuration(revealDuration);
         revealAnim.setInterpolator(revealInterpolator);
 
-        Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
-        fadeIn.setDuration(revealDuration);
+        ValueAnimator fadeIn = ValueAnimator.ofFloat(0, 1);
+        fadeIn.setDuration(revealDuration + arrowDuration);
         fadeIn.setInterpolator(revealInterpolator);
+        fadeIn.addUpdateListener(anim -> {
+            float alpha = (float) anim.getAnimatedValue();
+            mArrow.setAlpha(alpha);
+            setAlpha(revealAnim.isStarted() ? alpha : 0);
+        });
         openAnim.play(fadeIn);
 
         // Animate the arrow.
         mArrow.setScaleX(0);
         mArrow.setScaleY(0);
         Animator arrowScale = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 1)
-                .setDuration(res.getInteger(R.integer.config_popupArrowOpenDuration));
+                .setDuration(arrowDuration);
 
         openAnim.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -371,7 +384,7 @@
         });
 
         mOpenCloseAnimator = openAnim;
-        openAnim.playSequentially(revealAnim, arrowScale);
+        openAnim.playSequentially(arrowScale, revealAnim);
         openAnim.start();
     }
 
@@ -388,26 +401,35 @@
         }
         mIsOpen = false;
 
-        final AnimatorSet closeAnim = new AnimatorSet();
-        // Hide the arrow
-        closeAnim.play(ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 0));
-        closeAnim.play(ObjectAnimator.ofFloat(mArrow, ALPHA, 0));
 
+        final AnimatorSet closeAnim = new AnimatorSet();
         final Resources res = getResources();
-        final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+        final TimeInterpolator revealInterpolator = ACCEL_DEACCEL;
+        final long revealDuration = res.getInteger(R.integer.config_popupOpenCloseDuration);
+        final long arrowDuration = res.getInteger(R.integer.config_popupArrowOpenCloseDuration);
+
+        // Hide the arrow
+        Animator scaleArrow = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 0)
+                .setDuration(arrowDuration);
 
         // Rectangular reveal (reversed).
         final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
                 .createRevealAnimator(this, true);
+        revealAnim.setDuration(revealDuration);
         revealAnim.setInterpolator(revealInterpolator);
-        closeAnim.play(revealAnim);
+        closeAnim.playSequentially(revealAnim, scaleArrow);
 
-        Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
+        ValueAnimator fadeOut = ValueAnimator.ofFloat(getAlpha(), 0);
+        fadeOut.setDuration(revealDuration + arrowDuration);
         fadeOut.setInterpolator(revealInterpolator);
+        fadeOut.addUpdateListener(anim -> {
+            float alpha = (float) anim.getAnimatedValue();
+            mArrow.setAlpha(alpha);
+            setAlpha(scaleArrow.isStarted() ? 0 : alpha);
+        });
         closeAnim.play(fadeOut);
 
         onCreateCloseAnimation(closeAnim);
-        closeAnim.setDuration((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
         closeAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -429,21 +451,25 @@
     protected void onCreateCloseAnimation(AnimatorSet anim) { }
 
     private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
-        int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
+        Resources res = getResources();
+        int arrowCenterX = res.getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
                 R.dimen.popup_arrow_horizontal_center_start:
                 R.dimen.popup_arrow_horizontal_center_end);
+        int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
+        float arrowCornerRadius = res.getDimension(R.dimen.popup_arrow_corner_radius);
         if (!mIsLeftAligned) {
             arrowCenterX = getMeasuredWidth() - arrowCenterX;
         }
         int arrowCenterY = mIsAboveIcon ? getMeasuredHeight() : 0;
 
-        mStartRect.set(arrowCenterX, arrowCenterY, arrowCenterX, arrowCenterY);
+        mStartRect.set(arrowCenterX - halfArrowWidth, arrowCenterY, arrowCenterX + halfArrowWidth,
+                arrowCenterY);
         if (mEndRect.isEmpty()) {
             mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
         }
 
         return new RoundedRectRevealOutlineProvider
-                (mOutlineRadius, mOutlineRadius, mStartRect, mEndRect);
+                (arrowCornerRadius, mOutlineRadius, mStartRect, mEndRect);
     }
 
     /**