Implement parallax when dismissing docked/fullscreen stack

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
This commit is contained in:
Jorim Jaggi
2015-12-30 13:54:32 +01:00
parent 5098159ae3
commit e435e982fa
3 changed files with 134 additions and 9 deletions

View 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;

View 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);

View 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());
}