Merge "Fixing arrow navigation in task carousel" into ub-launcher3-edmonton
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 2e31ef2..6703bb5 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -184,7 +184,8 @@
                         // before our internal listeners.
                         mLauncher.getStateManager().setCurrentAnimation(anim);
 
-                        anim.play(getIconAnimator(v));
+                        Rect windowTargetBounds = getWindowTargetBounds(targetCompats);
+                        anim.play(getIconAnimator(v, windowTargetBounds));
                         if (launcherClosing) {
                             Pair<AnimatorSet, Runnable> launcherContentAnimator =
                                     getLauncherContentAnimator(true /* isAppOpening */);
@@ -196,7 +197,7 @@
                                 }
                             });
                         }
-                        anim.play(getOpeningWindowAnimators(v, targetCompats));
+                        anim.play(getOpeningWindowAnimators(v, targetCompats, windowTargetBounds));
                     }
 
                     if (launcherClosing) {
@@ -213,7 +214,26 @@
             return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
                     runner, duration, statusBarTransitionDelay));
         }
-        return getDefaultActivityLaunchOptions(launcher, v);
+        return super.getActivityLaunchOptions(launcher, v);
+    }
+
+    /**
+     * Return the window bounds of the opening target.
+     * In multiwindow mode, we need to get the final size of the opening app window target to help
+     * figure out where the floating view should animate to.
+     */
+    private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] targets) {
+        Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
+        if (mLauncher.isInMultiWindowModeCompat()) {
+            for (RemoteAnimationTargetCompat target : targets) {
+                if (target.mode == MODE_OPENING) {
+                    bounds.set(target.sourceContainerBounds);
+                    bounds.offsetTo(target.position.x, target.position.y);
+                    return bounds;
+                }
+            }
+        }
+        return bounds;
     }
 
     public void setRemoteAnimationProvider(RemoteAnimationProvider animationProvider) {
@@ -382,7 +402,7 @@
     /**
      * @return Animator that controls the icon used to launch the target.
      */
-    private AnimatorSet getIconAnimator(View v) {
+    private AnimatorSet getIconAnimator(View v, Rect windowTargetBounds) {
         final boolean isBubbleTextView = v instanceof BubbleTextView;
         mFloatingView = new View(mLauncher);
         if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
@@ -418,7 +438,7 @@
         viewLocationLeft += rect.left;
         viewLocationTop += rect.top;
         int viewLocationStart = mIsRtl
-                ? mDeviceProfile.widthPx - rect.right
+                ? windowTargetBounds.width() - rect.right
                 : viewLocationLeft;
         LayoutParams lp = new LayoutParams(rect.width(), rect.height());
         lp.ignoreInsets = true;
@@ -438,12 +458,15 @@
         v.setVisibility(View.INVISIBLE);
 
         AnimatorSet appIconAnimatorSet = new AnimatorSet();
-        // Animate the app icon to the center
-        float centerX = mDeviceProfile.widthPx / 2;
-        float centerY = mDeviceProfile.heightPx / 2;
+        int[] dragLayerBounds = new int[2];
+        mDragLayer.getLocationOnScreen(dragLayerBounds);
+
+        // Animate the app icon to the center of the window bounds in screen coordinates.
+        float centerX = windowTargetBounds.centerX() - dragLayerBounds[0];
+        float centerY = windowTargetBounds.centerY() - dragLayerBounds[1];
 
         float xPosition = mIsRtl
-                ? mDeviceProfile.widthPx - lp.getMarginStart() - rect.width()
+                ? windowTargetBounds.width() - lp.getMarginStart() - rect.width()
                 : lp.getMarginStart();
         float dX = centerX - xPosition - (lp.width / 2);
         float dY = centerY - lp.topMargin - (lp.height / 2);
@@ -469,8 +492,8 @@
 
         // Scale the app icon to take up the entire screen. This simplifies the math when
         // animating the app window position / scale.
-        float maxScaleX = mDeviceProfile.widthPx / (float) rect.width();
-        float maxScaleY = mDeviceProfile.heightPx / (float) rect.height();
+        float maxScaleX = windowTargetBounds.width() / (float) rect.width();
+        float maxScaleY = windowTargetBounds.height() / (float) rect.height();
         float scale = Math.max(maxScaleX, maxScaleY);
         ObjectAnimator scaleAnim = ObjectAnimator
                 .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale);
@@ -505,7 +528,8 @@
     /**
      * @return Animator that controls the window of the opening targets.
      */
-    private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) {
+    private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
+            Rect windowTargetBounds) {
         Rect bounds = new Rect();
         if (v.getParent() instanceof DeepShortcutView) {
             // Deep shortcut views have their icon drawn in a separate view.
@@ -544,31 +568,35 @@
                 float iconHeight = bounds.height() * mFloatingView.getScaleY();
 
                 // Scale the app window to match the icon size.
-                float scaleX = iconWidth / mDeviceProfile.widthPx;
-                float scaleY = iconHeight / mDeviceProfile.heightPx;
+                float scaleX = iconWidth / windowTargetBounds.width();
+                float scaleY = iconHeight / windowTargetBounds.height();
                 float scale = Math.min(1f, Math.min(scaleX, scaleY));
                 matrix.setScale(scale, scale);
 
                 // Position the scaled window on top of the icon
-                int deviceWidth = mDeviceProfile.widthPx;
-                int deviceHeight = mDeviceProfile.heightPx;
-                float scaledWindowWidth = deviceWidth * scale;
-                float scaledWindowHeight = deviceHeight * scale;
+                int windowWidth = windowTargetBounds.width();
+                int windowHeight = windowTargetBounds.height();
+                float scaledWindowWidth = windowWidth * scale;
+                float scaledWindowHeight = windowHeight * scale;
 
                 float offsetX = (scaledWindowWidth - iconWidth) / 2;
                 float offsetY = (scaledWindowHeight - iconHeight) / 2;
-                mFloatingView.getLocationInWindow(floatingViewBounds);
+                if (mLauncher.isInMultiWindowModeCompat()) {
+                    mFloatingView.getLocationOnScreen(floatingViewBounds);
+                } else {
+                    mFloatingView.getLocationInWindow(floatingViewBounds);
+                }
                 float transX0 = floatingViewBounds[0] - offsetX;
                 float transY0 = floatingViewBounds[1] - offsetY;
                 matrix.postTranslate(transX0, transY0);
 
                 // Animate the window crop so that it starts off as a square, and then reveals
                 // horizontally.
-                float cropHeight = deviceHeight * easePercent + deviceWidth * (1 - easePercent);
-                float initialTop = (deviceHeight - deviceWidth) / 2f;
+                float cropHeight = windowHeight * easePercent + windowWidth * (1 - easePercent);
+                float initialTop = (windowHeight - windowWidth) / 2f;
                 crop.left = 0;
                 crop.top = (int) (initialTop * (1 - easePercent));
-                crop.right = deviceWidth;
+                crop.right = windowWidth;
                 crop.bottom = (int) (crop.top + cropHeight);
 
                 TransactionCompat t = new TransactionCompat();
@@ -579,10 +607,6 @@
                 for (RemoteAnimationTargetCompat target : targets) {
                     if (target.mode == MODE_OPENING) {
                         t.setAlpha(target.leash, mAlpha.value);
-
-                        // TODO: This isn't correct at the beginning of the animation, but better
-                        // than nothing.
-                        matrix.postTranslate(target.position.x, target.position.y);
                         t.setMatrix(target.leash, matrix);
                         t.setWindowCrop(target.leash, crop);
                         t.deferTransactionUntil(target.leash, surface, getNextFrameNumber(surface));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
index 68773b4..6d10619 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
@@ -72,7 +72,7 @@
     @Override
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         super.onSwipeInteractionCompleted(targetState, logAction);
-        if (mFromState == NORMAL && targetState == OVERVIEW) {
+        if (mStartState == NORMAL && targetState == OVERVIEW) {
             RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG);
         }
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 987f952..3fb7cd4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -202,7 +202,7 @@
     @Override
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         super.onSwipeInteractionCompleted(targetState, logAction);
-        if (mFromState == NORMAL && targetState == OVERVIEW) {
+        if (mStartState == NORMAL && targetState == OVERVIEW) {
             RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG);
         }
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index b472d61..3adb290 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -176,8 +176,8 @@
     }
 
     @Override
-    public ActivityOptions getActivityLaunchOptions(final View v, boolean useDefaultLaunchOptions) {
-        if (useDefaultLaunchOptions || !(v instanceof TaskView)) {
+    public ActivityOptions getActivityLaunchOptions(final View v) {
+        if (!(v instanceof TaskView)) {
             return null;
         }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 2f1a0fe..0df0580 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -169,7 +169,7 @@
             final ActivityOptions opts;
             if (animate) {
                 opts = BaseDraggingActivity.fromContext(getContext())
-                        .getActivityLaunchOptions(this, false);
+                        .getActivityLaunchOptions(this);
             } else {
                 opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0);
             }
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index e47dbe5..d9e7d20 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -141,13 +141,12 @@
         return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
     }
 
-    public final Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) {
-        ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions);
+    public final Bundle getActivityLaunchOptionsAsBundle(View v) {
+        ActivityOptions activityOptions = getActivityLaunchOptions(v);
         return activityOptions == null ? null : activityOptions.toBundle();
     }
 
-    public abstract ActivityOptions getActivityLaunchOptions(
-            View v, boolean useDefaultLaunchOptions);
+    public abstract ActivityOptions getActivityLaunchOptions(View v);
 
     public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
         if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
@@ -160,7 +159,7 @@
         boolean useLaunchAnimation = (v != null) &&
                 !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
         Bundle optsBundle = useLaunchAnimation
-                ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
+                ? getActivityLaunchOptionsAsBundle(v)
                 : null;
 
         UserHandle user = item == null ? null : item.user;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 14390ec..bddcde3 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1614,10 +1614,8 @@
 
     @TargetApi(Build.VERSION_CODES.M)
     @Override
-    public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
-        return useDefaultLaunchOptions
-                ? mAppTransitionManager.getDefaultActivityLaunchOptions(this, v)
-                : mAppTransitionManager.getActivityLaunchOptions(this, v);
+    public ActivityOptions getActivityLaunchOptions(View v) {
+        return mAppTransitionManager.getActivityLaunchOptions(this, v);
     }
 
     public LauncherAppTransitionManager getAppTransitionManager() {
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index 04f9b3a..4037a23 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -33,7 +33,7 @@
                 context, R.string.app_transition_manager_class);
     }
 
-    public ActivityOptions getDefaultActivityLaunchOptions(Launcher launcher, View v) {
+    public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
         if (Utilities.ATLEAST_MARSHMALLOW) {
             int left = 0, top = 0;
             int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
@@ -58,8 +58,4 @@
         }
         return null;
     }
-
-    public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
-        return getDefaultActivityLaunchOptions(launcher, v);
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index f8648bb..68ad6e3 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -15,12 +15,14 @@
  */
 package com.android.launcher3.allapps;
 
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Process;
+import android.support.animation.DynamicAnimation;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
@@ -61,6 +63,10 @@
 public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
         Insettable, OnDeviceProfileChangeListener {
 
+    private static final float FLING_VELOCITY_MULTIPLIER = 135f;
+    // Starts the springs after at least 55% of the animation has passed.
+    private static final float FLING_ANIMATION_THRESHOLD = 0.55f;
+
     private final Launcher mLauncher;
     private final AdapterHolder[] mAH;
     private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
@@ -455,6 +461,32 @@
         }
     }
 
+    /**
+     * Adds an update listener to {@param animator} that adds springs to the animation.
+     */
+    public void addSpringFromFlingUpdateListener(ValueAnimator animator, float velocity) {
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                if (valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
+                    int searchViewId = getSearchView().getId();
+                    addSpringView(searchViewId);
+
+                    finishWithShiftAndVelocity(1, velocity * FLING_VELOCITY_MULTIPLIER,
+                            new DynamicAnimation.OnAnimationEndListener() {
+                                @Override
+                                public void onAnimationEnd(DynamicAnimation animation,
+                                        boolean canceled, float value, float velocity) {
+                                    removeSpringView(searchViewId);
+                                }
+                            });
+
+                    animator.removeUpdateListener(this);
+                }
+            }
+        });
+    }
+
     public class AdapterHolder {
         public static final int MAIN = 0;
         public static final int WORK = 1;
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index a20149e..3c1cc90 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -76,7 +76,7 @@
                 BaseDraggingActivity activity, ItemInfo itemInfo) {
             return (view) -> {
                 Rect sourceBounds = activity.getViewBounds(view);
-                Bundle opts = activity.getActivityLaunchOptionsAsBundle(view, false);
+                Bundle opts = activity.getActivityLaunchOptionsAsBundle(view);
                 new PackageManagerHelper(activity).startDetailsActivityForInfo(
                         itemInfo, sourceBounds, opts);
                 activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 24382b7..a9006e3 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -72,6 +72,7 @@
     private boolean mNoIntercept;
     protected int mStartContainerType;
 
+    protected LauncherState mStartState;
     protected LauncherState mFromState;
     protected LauncherState mToState;
     protected AnimatorPlaybackController mCurrentAnimation;
@@ -235,8 +236,10 @@
 
     @Override
     public void onDragStart(boolean start) {
+        mStartState = mLauncher.getStateManager().getState();
         if (mCurrentAnimation == null) {
-            mFromState = mToState = null;
+            mFromState = mStartState;
+            mToState = null;
             mAtomicComponentsController = null;
             reinitCurrentAnimation(false, mDetector.wasInitialTouchPositive());
             mDisplacementShift = 0;
@@ -396,6 +399,9 @@
         updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
                 targetState, velocity, fling);
         mCurrentAnimation.dispatchOnStart();
+        if (fling && targetState == LauncherState.ALL_APPS) {
+            mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
+        }
         anim.start();
         if (mAtomicAnim == null) {
             startAtomicComponentsAnim(endProgress, anim.getDuration());
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index 598738b..5022d65 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
+import android.support.animation.DynamicAnimation;
 import android.support.animation.FloatPropertyCompat;
 import android.support.animation.SpringAnimation;
 import android.support.animation.SpringForce;
@@ -79,6 +80,11 @@
         mSpringViews.put(id, true);
     }
 
+    public void removeSpringView(int id) {
+        mSpringViews.delete(id);
+        invalidate();
+    }
+
     @Override
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
@@ -110,6 +116,13 @@
         mSpring.start();
     }
 
+    protected void finishWithShiftAndVelocity(float shift, float velocity,
+            DynamicAnimation.OnAnimationEndListener listener) {
+        setDampedScrollShift(shift);
+        mSpring.addEndListener(listener);
+        finishScrollWithVelocity(velocity);
+    }
+
     public EdgeEffectFactory createEdgeEffectFactory() {
         return new SpringEdgeEffectFactory();
     }