Adding feature to support two different swipe targets from all-apps and overview

Change-Id: I7e7b4abbcebcbd6de43805c57ee40b0edd5ba5aa
diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
new file mode 100644
index 0000000..c1b26d4
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.touch.SwipeDetector.DIRECTION_NEGATIVE;
+import static com.android.launcher3.touch.SwipeDetector.DIRECTION_POSITIVE;
+import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
+import static com.android.launcher3.touch.SwipeDetector.VERTICAL;
+import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
+
+import android.graphics.Rect;
+import android.view.MotionEvent;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.VerticalSwipeController;
+import com.android.quickstep.RecentsView;
+
+/**
+ * Extension of {@link VerticalSwipeController} to go from NORMAL to OVERVIEW.
+ */
+public class EdgeSwipeController extends VerticalSwipeController {
+
+    private final Rect mTempRect = new Rect();
+
+    public EdgeSwipeController(Launcher l) {
+        super(l, NORMAL, OVERVIEW, l.getDeviceProfile().isVerticalBarLayout()
+                ? HORIZONTAL : VERTICAL);
+    }
+
+    @Override
+    protected boolean shouldInterceptTouch(MotionEvent ev) {
+        return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+    }
+
+    @Override
+    protected int getSwipeDirection(MotionEvent ev) {
+        return isTransitionFlipped() ? DIRECTION_NEGATIVE : DIRECTION_POSITIVE;
+    }
+
+    @Override
+    protected boolean isTransitionFlipped() {
+        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            Rect insets = mLauncher.getDragLayer().getInsets();
+            return insets.left > insets.right;
+        }
+        return false;
+    }
+
+    @Override
+    protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
+        // TODO: Log something
+    }
+
+    @Override
+    protected void initSprings() {
+        mSpringHandlers = new SpringAnimationHandler[0];
+    }
+
+    @Override
+    protected float getShiftRange() {
+        RecentsView.getPageRect(mLauncher, mTempRect);
+        DragLayer dl = mLauncher.getDragLayer();
+        Rect insets = dl.getInsets();
+
+        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            if (insets.left > insets.right) {
+                return insets.left + mTempRect.left;
+            } else {
+                return dl.getWidth() - mTempRect.right + insets.right;
+            }
+        } else {
+            return dl.getHeight() - mTempRect.bottom + insets.bottom;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index f59f0de..bdae2d6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
+import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -42,6 +43,7 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -90,6 +92,7 @@
     private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2;
     private static final int FLAG_RECENTS_PLAN_LOADING = 1 << 3;
     private static final int FLAG_OVERVIEW_DISABLED = 1 << 4;
+    private static final int FLAG_DISABLED_TWO_TARGETS = 1 << 5;
 
     private final Launcher mLauncher;
     private final SwipeDetector mDetector;
@@ -120,18 +123,25 @@
     }
 
     private boolean canInterceptTouch(MotionEvent ev) {
-        if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) {
-            // Don't listen for the swipe gesture if we are already in some other state.
-            return false;
-        }
-        if (mAnimatingToOverview) {
-            return false;
-        }
         if (mCurrentAnimation != null) {
             // If we are already animating from a previous state, we can intercept.
             return true;
         }
-        if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) {
+        if (mLauncher.isInState(NORMAL)) {
+            if ((ev.getEdgeFlags() & EDGE_NAV_BAR) != 0 &&
+                    !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+                // On normal swipes ignore edge swipes
+                return false;
+            }
+        } else if (mLauncher.isInState(ALL_APPS)) {
+            if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
+                return false;
+            }
+        } else {
+            // Don't listen for the swipe gesture if we are already in some other state.
+            return false;
+        }
+        if (mAnimatingToOverview) {
             return false;
         }
         if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
@@ -237,6 +247,10 @@
 
             mDragPauseDetector = new DragPauseDetector(this::onDragPauseDetected);
             mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE);
+            if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
+                mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_TWO_TARGETS);
+            }
+
             mOverviewProgressRange = new FloatRange();
             mOverviewProgressRange.start = mLauncher.isInState(NORMAL)
                     ? MIN_PROGRESS_TO_OVERVIEW
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 05bd171..bd443aa 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -37,9 +37,16 @@
     public static final boolean USE_HARDWARE_BITMAP = false; // FeatureFlags.IS_DOGFOOD_BUILD;
 
     public static TouchController[] createTouchControllers(Launcher launcher) {
-        return new TouchController[] {
-                new TwoStepSwipeController(launcher),
-                new OverviewSwipeUpController(launcher)};
+        if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
+            return new TouchController[]{
+                    new EdgeSwipeController(launcher),
+                    new TwoStepSwipeController(launcher),
+                    new OverviewSwipeUpController(launcher)};
+        } else {
+            return new TouchController[]{
+                    new TwoStepSwipeController(launcher),
+                    new OverviewSwipeUpController(launcher)};
+        }
     }
 
     public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4321791..c72fbf4 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -64,6 +64,8 @@
 @TargetApi(Build.VERSION_CODES.O)
 public class TouchInteractionService extends Service {
 
+    public static final int EDGE_NAV_BAR = 1 << 8;
+
     private static final String TAG = "TouchInteractionService";
 
     private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@@ -381,9 +383,12 @@
         }
 
         private void sendEvent(MotionEvent ev) {
+            int flags = ev.getEdgeFlags();
+            ev.setEdgeFlags(flags | EDGE_NAV_BAR);
             ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
             mTarget.dispatchTouchEvent(ev);
             ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
+            ev.setEdgeFlags(flags);
         }
     }
 }
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 18797a4..2d0e630 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -57,4 +57,6 @@
     public static final boolean ALL_APPS_TABS_ENABLED = true;
     // When enabled prediction row is rendered as it's own custom view
     public static final boolean ALL_APPS_PREDICTION_ROW_VIEW = true;
+
+    public static final boolean ENABLE_TWO_SWIPE_TARGETS = true;
 }
diff --git a/src/com/android/launcher3/util/VerticalSwipeController.java b/src/com/android/launcher3/util/VerticalSwipeController.java
index 7b1632c..5d47cd2 100644
--- a/src/com/android/launcher3/util/VerticalSwipeController.java
+++ b/src/com/android/launcher3/util/VerticalSwipeController.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.SpringAnimationHandler;
 import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.touch.SwipeDetector.Direction;
 
 import java.util.ArrayList;
 
@@ -56,6 +57,7 @@
     protected final Launcher mLauncher;
     private final SwipeDetector mDetector;
     private final LauncherState mBaseState;
+    private final LauncherState mTargetState;
 
     private boolean mNoIntercept;
 
@@ -66,12 +68,18 @@
     // Ratio of transition process [0, 1] to drag displacement (px)
     private float mProgressMultiplier;
 
-    private SpringAnimationHandler[] mSpringHandlers;
+    protected SpringAnimationHandler[] mSpringHandlers;
 
     public VerticalSwipeController(Launcher l, LauncherState baseState) {
+        this(l, baseState, ALL_APPS, SwipeDetector.VERTICAL);
+    }
+
+    public VerticalSwipeController(
+            Launcher l, LauncherState baseState, LauncherState targetState, Direction dir) {
         mLauncher = l;
-        mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
+        mDetector = new SwipeDetector(l, this, dir);
         mBaseState = baseState;
+        mTargetState = targetState;
     }
 
     private boolean canInterceptTouch(MotionEvent ev) {
@@ -96,7 +104,7 @@
         }
     }
 
-    private void initSprings() {
+    protected void initSprings() {
         AllAppsContainerView appsView = mLauncher.getAppsView();
 
         SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
@@ -178,12 +186,13 @@
             long maxAccuracy = (long) (2 * range);
 
             // Build current animation
-            mToState = mLauncher.isInState(ALL_APPS) ? mBaseState : ALL_APPS;
+            mToState = mLauncher.isInState(mTargetState) ? mBaseState : mTargetState;
             mCurrentAnimation = mLauncher.getStateManager()
                     .createAnimationToNewWorkspace(mToState, maxAccuracy);
             mCurrentAnimation.getTarget().addListener(this);
             mStartProgress = 0;
-            mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range;
+            mProgressMultiplier =
+                    (mLauncher.isInState(mTargetState) ^ isTransitionFlipped() ? 1 : -1) / range;
             mCurrentAnimation.dispatchOnStart();
         } else {
             mCurrentAnimation.pause();
@@ -195,7 +204,11 @@
         }
     }
 
-    private float getShiftRange() {
+    protected boolean isTransitionFlipped() {
+        return false;
+    }
+
+    protected float getShiftRange() {
         return mLauncher.getAllAppsController().getShiftRange();
     }
 
@@ -213,27 +226,25 @@
         final float progress = mCurrentAnimation.getProgressFraction();
 
         if (fling) {
-            if (velocity < 0) {
-                targetState = ALL_APPS;
-                animationDuration = SwipeDetector.calculateDuration(velocity,
-                        mToState == ALL_APPS ? (1 - progress) : progress);
+            if (velocity < 0 ^ isTransitionFlipped()) {
+                targetState = mTargetState;
             } else {
                 targetState = mBaseState;
-                animationDuration = SwipeDetector.calculateDuration(velocity,
-                        mToState == ALL_APPS ? progress : (1 - progress));
             }
+            animationDuration = SwipeDetector.calculateDuration(velocity,
+                    mToState == targetState ? (1 - progress) : progress);
             // snap to top or bottom using the release velocity
         } else {
             if (progress > SUCCESS_TRANSITION_PROGRESS) {
                 targetState = mToState;
                 animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress);
             } else {
-                targetState = mToState == ALL_APPS ? mBaseState : ALL_APPS;
+                targetState = mToState == mTargetState ? mBaseState : mTargetState;
                 animationDuration = SwipeDetector.calculateDuration(velocity, progress);
             }
         }
 
-        if (fling && targetState == ALL_APPS) {
+        if (fling && targetState == mTargetState) {
             for (SpringAnimationHandler h : mSpringHandlers) {
                 // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
                 h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);