OSDN Git Service

Implement parallax when dismissing docked/fullscreen stack
authorJorim Jaggi <jjaggi@google.com>
Wed, 30 Dec 2015 12:54:32 +0000 (13:54 +0100)
committerJorim Jaggi <jjaggi@google.com>
Tue, 5 Jan 2016 12:50:45 +0000 (13:50 +0100)
When moving the docked or the fullscreen task close to the side,
we add a nice parallax to indicate that this task will be dismissed.

Change-Id: Ide195876942c1614c186fd5f3ff3e86f6fdfec61

packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java

index 8d42792..e43d531 100644 (file)
@@ -118,6 +118,22 @@ public class DividerSnapAlgorithm {
         }
     }
 
+    public SnapTarget getFirstSplitTarget() {
+        return mFirstSplitTarget;
+    }
+
+    public SnapTarget getLastSplitTarget() {
+        return mLastSplitTarget;
+    }
+
+    public SnapTarget getDismissStartTarget() {
+        return mDismissStartTarget;
+    }
+
+    public SnapTarget getDismissEndTarget() {
+        return mDismissEndTarget;
+    }
+
     private SnapTarget snap(int position) {
         int minIndex = -1;
         int minDistance = Integer.MAX_VALUE;
index 8d94d5e..3b4c596 100644 (file)
@@ -28,6 +28,7 @@ import android.graphics.Rect;
 import android.graphics.Region.Op;
 import android.hardware.display.DisplayManager;
 import android.util.AttributeSet;
+import android.util.MathUtils;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.MotionEvent;
@@ -37,6 +38,7 @@ import android.view.View;
 import android.view.View.OnTouchListener;
 import android.view.ViewTreeObserver.InternalInsetsInfo;
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.animation.AnimationUtils;
@@ -64,6 +66,9 @@ public class DividerView extends FrameLayout implements OnTouchListener,
     private static final float DIM_START_FRACTION = 0.5f;
     private static final float DIM_DAMP_FACTOR = 1.7f;
 
+    private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
+            new PathInterpolator(0.5f, 1f, 0.5f, 1f);
+
     private ImageButton mHandle;
     private View mBackground;
     private int mStartX;
@@ -202,7 +207,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                     int position = calculatePosition(x, y);
                     SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position,
                             0 /* velocity */);
-                    resizeStack(calculatePosition(x, y), snapTarget.position);
+                    resizeStack(calculatePosition(x, y), snapTarget.position, snapTarget);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -235,7 +240,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                 resizeStack((Integer) animation.getAnimatedValue(),
                         animation.getAnimatedFraction() == 1f
                                 ? TASK_POSITION_SAME
-                                : snapTarget.position);
+                                : snapTarget.position, snapTarget);
             }
         });
         anim.addListener(new AnimatorListenerAdapter() {
@@ -393,7 +398,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                 containingRect.right, containingRect.bottom);
     }
 
-    public void resizeStack(int position, int taskPosition) {
+    public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
         calculateBoundsForPosition(position, mDockSide, mDockedRect);
 
         if (mDockedRect.equals(mLastResizeRect)) {
@@ -406,19 +411,28 @@ public class DividerView extends FrameLayout implements OnTouchListener,
         mLastResizeRect.set(mDockedRect);
         if (taskPosition != TASK_POSITION_SAME) {
             calculateBoundsForPosition(position, invertDockSide(mDockSide), mOtherRect);
-            calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
-            calculateBoundsForPosition(taskPosition, invertDockSide(mDockSide), mOtherTaskRect);
+            int dockSideInverted = invertDockSide(mDockSide);
+            int taskPositionDocked =
+                    restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget);
+            int taskPositionOther =
+                    restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
+            calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
+            calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
             alignTopLeft(mDockedRect, mDockedTaskRect);
             alignTopLeft(mOtherRect, mOtherTaskRect);
             mDockedInsetRect.set(mDockedTaskRect);
             mOtherInsetRect.set(mOtherTaskRect);
-            if (mDockSide == WindowManager.DOCKED_LEFT || mDockSide == WindowManager.DOCKED_TOP) {
+            if (dockSideTopLeft(mDockSide)) {
                 alignTopLeft(mDockedRect, mDockedInsetRect);
                 alignBottomRight(mOtherRect, mOtherInsetRect);
             } else {
                 alignBottomRight(mDockedRect, mDockedInsetRect);
                 alignTopLeft(mOtherRect, mOtherInsetRect);
             }
+            applyDismissingParallax(mDockedTaskRect, mDockSide, taskSnapTarget, position,
+                    taskPositionDocked);
+            applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position,
+                    taskPositionOther);
             mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
                     mOtherTaskRect, mOtherInsetRect);
         } else {
@@ -432,6 +446,86 @@ public class DividerView extends FrameLayout implements OnTouchListener,
                 fraction);
     }
 
+    /**
+     * When the snap target is dismissing one side, make sure that the dismissing side doesn't get
+     * 0 size.
+     */
+    private int restrictDismissingTaskPosition(int taskPosition, int dockSide,
+            SnapTarget snapTarget) {
+        if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) {
+            return mSnapAlgorithm.getFirstSplitTarget().position;
+        } else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END
+                && dockSideBottomRight(dockSide)) {
+            return mSnapAlgorithm.getLastSplitTarget().position;
+        } else {
+            return taskPosition;
+        }
+    }
+
+    /**
+     * Applies a parallax to the task when dismissing.
+     */
+    private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget,
+            int position, int taskPosition) {
+        float fraction = Math.min(1, Math.max(0,
+                mSnapAlgorithm.calculateDismissingFraction(position)));
+        SnapTarget dismissTarget = null;
+        SnapTarget splitTarget = null;
+        if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_START
+                || snapTarget == mSnapAlgorithm.getFirstSplitTarget())
+                && dockSideTopLeft(dockSide)) {
+            dismissTarget = mSnapAlgorithm.getDismissStartTarget();
+            splitTarget = mSnapAlgorithm.getFirstSplitTarget();
+        } else if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_END
+                || snapTarget == mSnapAlgorithm.getLastSplitTarget())
+                && dockSideBottomRight(dockSide)) {
+            dismissTarget = mSnapAlgorithm.getDismissEndTarget();
+            splitTarget = mSnapAlgorithm.getLastSplitTarget();
+        }
+        if (dismissTarget != null && fraction > 0f
+                && isDismissing(splitTarget, position, dockSide)) {
+            fraction = calculateParallaxDismissingFraction(fraction);
+            int offsetPosition = (int) (taskPosition +
+                    fraction * (dismissTarget.position - splitTarget.position));
+            int width = taskRect.width();
+            int height = taskRect.height();
+            switch (dockSide) {
+                case WindowManager.DOCKED_LEFT:
+                    taskRect.left = offsetPosition - width;
+                    taskRect.right = offsetPosition;
+                    break;
+                case WindowManager.DOCKED_RIGHT:
+                    taskRect.left = offsetPosition + mDividerSize;
+                    taskRect.right = offsetPosition + width + mDividerSize;
+                    break;
+                case WindowManager.DOCKED_TOP:
+                    taskRect.top = offsetPosition - height;
+                    taskRect.bottom = offsetPosition;
+                    break;
+                case WindowManager.DOCKED_BOTTOM:
+                    taskRect.top = offsetPosition + mDividerSize;
+                    taskRect.bottom = offsetPosition + height + mDividerSize;
+                    break;
+            }
+        }
+    }
+
+    /**
+     * @return for a specified {@code fraction}, this returns an adjusted value that simulates a
+     *         slowing down parallax effect
+     */
+    private static float calculateParallaxDismissingFraction(float fraction) {
+        return SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f;
+    }
+
+    private static boolean isDismissing(SnapTarget snapTarget, int position, int dockSide) {
+        if (dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT) {
+            return position < snapTarget.position;
+        } else {
+            return position > snapTarget.position;
+        }
+    }
+
     private int getStackIdForDismissTarget(SnapTarget dismissTarget) {
         if (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START &&
                 (mDockSide == WindowManager.DOCKED_LEFT || mDockSide == WindowManager.DOCKED_TOP)) {
@@ -441,6 +535,20 @@ public class DividerView extends FrameLayout implements OnTouchListener,
         }
     }
 
+    /**
+     * @return true if and only if {@code dockSide} is top or left
+     */
+    private static boolean dockSideTopLeft(int dockSide) {
+        return dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT;
+    }
+
+    /**
+     * @return true if and only if {@code dockSide} is bottom or right
+     */
+    private static boolean dockSideBottomRight(int dockSide) {
+        return dockSide == WindowManager.DOCKED_BOTTOM || dockSide == WindowManager.DOCKED_RIGHT;
+    }
+
     @Override
     public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) {
         inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
index c50db3b..3a30f34 100644 (file)
@@ -29,6 +29,7 @@ import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
 import com.android.systemui.stackdivider.DividerView;
 import com.android.systemui.tuner.TunerService;
 
@@ -200,9 +201,9 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL
         } else {
             if (mDragMode == DRAG_MODE_DIVIDER) {
                 int position = !mIsVertical ? (int) event.getRawY() : (int) event.getRawX();
-                mDivider.getView().resizeStack(
-                        position, mDivider.getView().getSnapAlgorithm()
-                                .calculateSnapTarget(position, 0f /* velocity */).position);
+                SnapTarget snapTarget = mDivider.getView().getSnapAlgorithm()
+                        .calculateSnapTarget(position, 0f /* velocity */);
+                mDivider.getView().resizeStack(position, snapTarget.position, snapTarget);
             } else if (mDragMode == DRAG_MODE_RECENTS) {
                 mRecentsComponent.onDraggingInRecents(event.getRawY());
             }