Merge "Fix bug where drag handle translation was not set to 0 on device profile change." into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
index 23add95..3622fc4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
@@ -37,12 +37,16 @@
 
     @Override
     protected int getSwipeDirection(MotionEvent ev) {
-        mFromState = NORMAL;
-        mToState = OVERVIEW;
         return SwipeDetector.DIRECTION_BOTH;
     }
 
     @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        boolean draggingFromNav = mLauncher.getDeviceProfile().isSeascape() != isDragTowardPositive;
+        return draggingFromNav ? OVERVIEW : NORMAL;
+    }
+
+    @Override
     protected float getShiftRange() {
         return mLauncher.getDragLayer().getWidth();
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
index 720b20a..355b88d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
@@ -58,8 +58,9 @@
         }
     }
 
-    protected LauncherState getTargetState() {
-        if (mLauncher.isInState(ALL_APPS)) {
+    @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        if (fromState == ALL_APPS) {
             // Should swipe down go to OVERVIEW instead?
             return TouchInteractionService.isConnected() ?
                     mLauncher.getStateManager().getLastState() : NORMAL;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 7d9cce4..0c34b45 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -32,22 +32,26 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.SysuiEventLogger;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
 
 /**
  * Touch controller for handling various state transitions in portrait UI.
  */
 public class PortraitStatesTouchController extends AbstractStateChangeTouchController {
 
-    private static final float TOTAL_DISTANCE_MULTIPLIER = 2f;
+    private static final float TOTAL_DISTANCE_MULTIPLIER = 3f;
     private static final float LINEAR_SCALE_LIMIT = 1 / TOTAL_DISTANCE_MULTIPLIER;
 
-    // Much be greater than LINEAR_SCALE_LIMIT;
+    // Must be greater than LINEAR_SCALE_LIMIT;
     private static final float MAXIMUM_DISTANCE_FACTOR = 0.9f;
 
     // Maximum amount to overshoot.
@@ -129,29 +133,26 @@
             directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
             mStartContainerType = ContainerType.HOTSEAT;
         } else if (mLauncher.isInState(OVERVIEW)) {
-            directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+            directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
             mStartContainerType = ContainerType.TASKSWITCHER;
         } else {
             return 0;
         }
-        mFromState = mLauncher.getStateManager().getState();
-        mToState = getTargetState();
-        if (mFromState == mToState) {
-            return 0;
-        }
         return directionsToDetectScroll;
     }
 
-    protected LauncherState getTargetState() {
-        if (mLauncher.isInState(ALL_APPS)) {
+    @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        if (fromState == ALL_APPS) {
             // Should swipe down go to OVERVIEW instead?
             return TouchInteractionService.isConnected() ?
                     mLauncher.getStateManager().getLastState() : NORMAL;
-        } else if (mLauncher.isInState(OVERVIEW)) {
-            return ALL_APPS;
-        } else {
+        } else if (fromState == OVERVIEW) {
+            return isDragTowardPositive ? ALL_APPS : NORMAL;
+        } else if (isDragTowardPositive) {
             return TouchInteractionService.isConnected() ? OVERVIEW : ALL_APPS;
         }
+        return fromState;
     }
 
     private AnimatorSetBuilder getNormalToOverviewAnimation() {
@@ -194,8 +195,23 @@
             mClampProgressUpdate = -1;
         }
 
-        mCurrentAnimation = mLauncher.getStateManager()
-                .createAnimationToNewWorkspace(mToState, builder, maxAccuracy);
+        if (mPendingAnimation != null) {
+            mPendingAnimation.finish(false);
+            mPendingAnimation = null;
+        }
+
+        RecentsView recentsView = mLauncher.getOverviewPanel();
+        TaskView taskView = (TaskView) recentsView.getChildAt(recentsView.getNextPage());
+        if (recentsView.shouldSwipeDownLaunchApp() && mFromState == OVERVIEW && mToState == NORMAL
+                && taskView != null) {
+            mPendingAnimation = recentsView.createTaskLauncherAnimation(taskView, maxAccuracy);
+            mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
+
+            mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy);
+        } else {
+            mCurrentAnimation = mLauncher.getStateManager()
+                    .createAnimationToNewWorkspace(mToState, builder, maxAccuracy);
+        }
 
         if (totalShift == 0) {
             totalShift = Math.signum(mFromState.ordinal - mToState.ordinal)
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index 7b2487a..e73b219 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -34,7 +34,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.PendingAnimation;
+import com.android.launcher3.util.PendingAnimation;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 3cae371..c1590f6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.Utilities.getPrefs;
 import static com.android.quickstep.OverviewInteractionState.KEY_SWIPE_UP_ENABLED;
@@ -25,7 +26,9 @@
 import android.content.SharedPreferences;
 
 import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.OverviewInteractionState;
@@ -89,11 +92,15 @@
     }
 
     public static void onLauncherStateOrResumeChanged(Launcher launcher) {
+        LauncherState state = launcher.getStateManager().getState();
+        DeviceProfile profile = launcher.getDeviceProfile();
         WindowManagerWrapper.getInstance().setShelfHeight(
-                launcher.getStateManager().getState() != ALL_APPS &&
-                        launcher.isUserActive() &&
-                        !launcher.getDeviceProfile().isVerticalBarLayout(),
-                launcher.getDeviceProfile().hotseatBarSizePx);
+                state != ALL_APPS && launcher.isUserActive() && !profile.isVerticalBarLayout(),
+                profile.hotseatBarSizePx);
+
+        if (state == NORMAL) {
+            launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
+        }
     }
 
     public static void onTrimMemory(Context context, int level) {
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 33f1310..611f7e0 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -762,6 +762,8 @@
         // Animate the first icon.
         mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */);
 
+        mRecentsView.setSwipeDownShouldLaunchApp(true);
+
         reset();
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 50a32e6..e7f69b7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -60,7 +60,7 @@
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Themes;
-import com.android.quickstep.PendingAnimation;
+import com.android.launcher3.util.PendingAnimation;
 import com.android.quickstep.QuickScrubController;
 import com.android.quickstep.RecentsAnimationInterpolator;
 import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
@@ -139,6 +139,7 @@
     private boolean mOverviewStateEnabled;
     private boolean mTaskStackListenerRegistered;
     private Runnable mNextPageSwitchRunnable;
+    private boolean mSwipeDownShouldLaunchApp;
 
     private PendingAnimation mPendingAnimation;
 
@@ -275,6 +276,9 @@
             mNextPageSwitchRunnable.run();
             mNextPageSwitchRunnable = null;
         }
+        if (getNextPage() > 0) {
+            setSwipeDownShouldLaunchApp(true);
+        }
     }
 
     @Override
@@ -601,6 +605,14 @@
         }
     }
 
+    public void setSwipeDownShouldLaunchApp(boolean swipeDownShouldLaunchApp) {
+        mSwipeDownShouldLaunchApp = swipeDownShouldLaunchApp;
+    }
+
+    public boolean shouldSwipeDownLaunchApp() {
+        return mSwipeDownShouldLaunchApp;
+    }
+
     public interface PageCallbacks {
 
         /**
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 21f9d5a..fdb6f48 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -18,7 +18,6 @@
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
 
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index a22f450..9726704 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -19,7 +19,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -31,6 +30,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.PendingAnimation;
 
 /**
  * TouchController for handling state changes
@@ -54,10 +54,12 @@
     protected LauncherState mFromState;
     protected LauncherState mToState;
     protected AnimatorPlaybackController mCurrentAnimation;
+    protected PendingAnimation mPendingAnimation;
 
     private float mStartProgress;
     // Ratio of transition process [0, 1] to drag displacement (px)
     private float mProgressMultiplier;
+    private float mDisplacementShift;
 
     public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
         mLauncher = l;
@@ -68,7 +70,7 @@
 
     /**
      * Initializes the {@code mFromState} and {@code mToState} and swipe direction to use for
-     * the detector. In can of disabling swipe, return 0.
+     * the detector. In case of disabling swipe, return 0.
      */
     protected abstract int getSwipeDirection(MotionEvent ev);
 
@@ -122,16 +124,36 @@
         return mLauncher.getAllAppsController().getShiftRange();
     }
 
+    protected abstract LauncherState getTargetState(LauncherState fromState,
+            boolean isDragTowardPositive);
+
     protected abstract float initCurrentAnimation();
 
+    private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
+        LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
+                : reachedToState ? mToState : mFromState;
+        LauncherState newToState = getTargetState(newFromState, isDragTowardPositive);
+
+        if (newFromState == mFromState && newToState == mToState || (newFromState == newToState)) {
+            return false;
+        }
+
+        mFromState = newFromState;
+        mToState = newToState;
+
+        mStartProgress = 0;
+        mProgressMultiplier = initCurrentAnimation();
+        mCurrentAnimation.getTarget().addListener(this);
+        mCurrentAnimation.dispatchOnStart();
+        return true;
+    }
+
     @Override
     public void onDragStart(boolean start) {
         if (mCurrentAnimation == null) {
-            mStartProgress = 0;
-            mProgressMultiplier = initCurrentAnimation();
-
-            mCurrentAnimation.getTarget().addListener(this);
-            mCurrentAnimation.dispatchOnStart();
+            mFromState = mToState = null;
+            reinitCurrentAnimation(false, mDetector.wasInitialTouchPositive());
+            mDisplacementShift = 0;
         } else {
             mCurrentAnimation.pause();
             mStartProgress = mCurrentAnimation.getProgressFraction();
@@ -140,8 +162,19 @@
 
     @Override
     public boolean onDrag(float displacement, float velocity) {
-        float deltaProgress = mProgressMultiplier * displacement;
-        updateProgress(deltaProgress + mStartProgress);
+        float deltaProgress = mProgressMultiplier * (displacement - mDisplacementShift);
+        float progress = deltaProgress + mStartProgress;
+        updateProgress(progress);
+        boolean isDragTowardPositive = (displacement - mDisplacementShift) < 0;
+        if (progress <= 0) {
+            if (reinitCurrentAnimation(false, isDragTowardPositive)) {
+                mDisplacementShift = displacement;
+            }
+        } else if (progress >= 1) {
+            if (reinitCurrentAnimation(true, isDragTowardPositive)) {
+                mDisplacementShift = displacement;
+            }
+        }
         return true;
     }
 
@@ -223,7 +256,16 @@
                     mLauncher.getWorkspace().getCurrentPage());
         }
         clearState();
-        mLauncher.getStateManager().goToState(targetState, false /* animated */);
+        boolean shouldGoToTargetState = true;
+        if (mPendingAnimation != null) {
+            boolean reachedTarget = mToState == targetState;
+            mPendingAnimation.finish(reachedTarget);
+            mPendingAnimation = null;
+            shouldGoToTargetState = !reachedTarget;
+        }
+        if (shouldGoToTargetState) {
+            mLauncher.getStateManager().goToState(targetState, false /* animated */);
+        }
     }
 
     protected void clearState() {
diff --git a/quickstep/src/com/android/quickstep/PendingAnimation.java b/src/com/android/launcher3/util/PendingAnimation.java
similarity index 97%
rename from quickstep/src/com/android/quickstep/PendingAnimation.java
rename to src/com/android/launcher3/util/PendingAnimation.java
index d22ef61..4116d56 100644
--- a/quickstep/src/com/android/quickstep/PendingAnimation.java
+++ b/src/com/android/launcher3/util/PendingAnimation.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.quickstep;
+package com.android.launcher3.util;
 
 import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
index e495477..c97c3cc 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
@@ -7,6 +7,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -43,12 +44,8 @@
     protected int getSwipeDirection(MotionEvent ev) {
         if (mLauncher.isInState(ALL_APPS)) {
             mStartContainerType = ContainerType.ALLAPPS;
-            mFromState = ALL_APPS;
-            mToState = NORMAL;
             return SwipeDetector.DIRECTION_NEGATIVE;
         } else {
-            mFromState = NORMAL;
-            mToState = ALL_APPS;
             mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ?
                     ContainerType.HOTSEAT : ContainerType.WORKSPACE;
             return SwipeDetector.DIRECTION_POSITIVE;
@@ -56,6 +53,11 @@
     }
 
     @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        return fromState == ALL_APPS ? NORMAL : ALL_APPS;
+    }
+
+    @Override
     protected float initCurrentAnimation() {
         float range = getShiftRange();
         long maxAccuracy = (long) (2 * range);