Merge "Add drag down to dismiss pip as a dismiss option"

This commit is contained in:
Mady Mellor
2017-03-10 22:11:52 +00:00
committed by Android (Google) Code Review
3 changed files with 105 additions and 35 deletions

View File

@@ -56,7 +56,8 @@ public class PipMotionHelper {
private static final int DEFAULT_MOVE_STACK_DURATION = 225;
private static final int SNAP_STACK_DURATION = 225;
private static final int DISMISS_STACK_DURATION = 375;
private static final int DRAG_TO_TARGET_DISMISS_STACK_DURATION = 375;
private static final int DRAG_TO_DISMISS_STACK_DURATION = 175;
private static final int SHRINK_STACK_FROM_MENU_DURATION = 250;
private static final int EXPAND_STACK_TO_MENU_DURATION = 250;
private static final int EXPAND_STACK_TO_FULLSCREEN_DURATION = 300;
@@ -65,6 +66,8 @@ public class PipMotionHelper {
// The fraction of the stack width that the user has to drag offscreen to minimize the PiP
private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f;
// The fraction of the stack height that the user has to drag offscreen to minimize the PiP
private static final float DISMISS_OFFSCREEN_FRACTION = 0.35f;
private Context mContext;
private IActivityManager mActivityManager;
@@ -193,6 +196,19 @@ public class PipMotionHelper {
}
}
/**
* @return whether the PiP at the current bounds should be dismissed.
*/
boolean shouldDismissPip() {
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
if (mBounds.bottom > displaySize.y) {
float offscreenFraction = (float) (mBounds.bottom - displaySize.y) / mBounds.height();
return offscreenFraction >= DISMISS_OFFSCREEN_FRACTION;
}
return false;
}
/**
* Flings the minimized PiP to the closest minimized snap target.
*/
@@ -297,16 +313,38 @@ public class PipMotionHelper {
resizeAndAnimatePipUnchecked(toBounds, IME_SHIFT_DURATION);
}
/**
* Animates the dismissal of the PiP off the edge of the screen.
*/
Rect animateDragToEdgeDismiss(Rect pipBounds) {
cancelAnimations();
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
Rect toBounds = new Rect(pipBounds);
toBounds.offset(0, displaySize.y - pipBounds.top);
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, DRAG_TO_DISMISS_STACK_DURATION,
FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dismissPip();
}
});
mBoundsAnimator.start();
return toBounds;
}
/**
* Animates the dismissal of the PiP over the dismiss target bounds.
*/
Rect animateDismissFromDrag(Rect dismissBounds) {
Rect animateDragToTargetDismiss(Rect dismissBounds) {
cancelAnimations();
Rect toBounds = new Rect(dismissBounds.centerX(),
dismissBounds.centerY(),
dismissBounds.centerX() + 1,
dismissBounds.centerY() + 1);
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, DISMISS_STACK_DURATION,
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
DRAG_TO_TARGET_DISMISS_STACK_DURATION,
FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
@Override

View File

@@ -52,7 +52,8 @@ public class PipTouchHandler {
private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 200;
// Allow dragging the PIP to a location to close it
private static final boolean ENABLE_DRAG_TO_DISMISS = false;
private static final boolean ENABLE_DISMISS_DRAG_TO_TARGET = false;
private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = false;
private final Context mContext;
private final IActivityManager mActivityManager;
@@ -78,7 +79,7 @@ public class PipTouchHandler {
private Runnable mShowDismissAffordance = new Runnable() {
@Override
public void run() {
if (ENABLE_DRAG_TO_DISMISS) {
if (ENABLE_DISMISS_DRAG_TO_TARGET) {
mDismissViewController.showDismissTarget(mMotionHelper.getBounds());
}
}
@@ -378,7 +379,7 @@ public class PipTouchHandler {
mMenuController.pokeMenu();
}
if (ENABLE_DRAG_TO_DISMISS) {
if (ENABLE_DISMISS_DRAG_TO_TARGET) {
mDismissViewController.createDismissTarget();
mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
}
@@ -394,7 +395,7 @@ public class PipTouchHandler {
mSavedSnapFraction = -1f;
}
if (touchState.startedDragging() && ENABLE_DRAG_TO_DISMISS) {
if (touchState.startedDragging() && ENABLE_DISMISS_DRAG_TO_TARGET) {
mHandler.removeCallbacks(mShowDismissAffordance);
mDismissViewController.showDismissTarget(mMotionHelper.getBounds());
}
@@ -408,11 +409,16 @@ public class PipTouchHandler {
if (!touchState.allowDraggingOffscreen()) {
left = Math.max(mMovementBounds.left, Math.min(mMovementBounds.right, left));
}
top = Math.max(mMovementBounds.top, Math.min(mMovementBounds.bottom, top));
if (ENABLE_DISMISS_DRAG_TO_EDGE) {
// Allow pip to move past bottom bounds
top = Math.max(mMovementBounds.top, top);
} else {
top = Math.max(mMovementBounds.top, Math.min(mMovementBounds.bottom, top));
}
mTmpBounds.offsetTo((int) left, (int) top);
mMotionHelper.movePip(mTmpBounds);
if (ENABLE_DRAG_TO_DISMISS) {
if (ENABLE_DISMISS_DRAG_TO_TARGET) {
mDismissViewController.updateDismissTarget(mTmpBounds);
}
return true;
@@ -427,7 +433,7 @@ public class PipTouchHandler {
}
try {
if (ENABLE_DRAG_TO_DISMISS) {
if (ENABLE_DISMISS_DRAG_TO_TARGET) {
mHandler.removeCallbacks(mShowDismissAffordance);
PointF vel = mTouchState.getVelocity();
final float velocity = PointF.length(vel.x, vel.y);
@@ -435,7 +441,7 @@ public class PipTouchHandler {
&& velocity < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
if (mDismissViewController.shouldDismiss(mMotionHelper.getBounds())) {
Rect dismissBounds = mDismissViewController.getDismissBounds();
mMotionHelper.animateDismissFromDrag(dismissBounds);
mMotionHelper.animateDragToTargetDismiss(dismissBounds);
MetricsLogger.action(mContext,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_DRAG);
@@ -448,9 +454,17 @@ public class PipTouchHandler {
}
if (touchState.isDragging()) {
PointF vel = mTouchState.getVelocity();
if (!mIsMinimized && (mMotionHelper.shouldMinimizePip()
|| isHorizontalFlingTowardsCurrentEdge(vel))) {
final boolean onLeft = mMotionHelper.getBounds().left < mMovementBounds.centerX();
boolean isFlingToBot = isFlingTowardsEdge(touchState, 4 /* bottom */);
if (ENABLE_DISMISS_DRAG_TO_EDGE
&& (mMotionHelper.shouldDismissPip() || isFlingToBot)) {
mMotionHelper.animateDragToEdgeDismiss(mMotionHelper.getBounds());
MetricsLogger.action(mContext,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_DRAG);
return true;
} else if (!mIsMinimized && (mMotionHelper.shouldMinimizePip()
|| isFlingTowardsEdge(touchState, onLeft ? 2 : 3))) {
// Pip should be minimized
setMinimizedStateInternal(true);
if (mMenuController.isMenuVisible()) {
@@ -475,6 +489,7 @@ public class PipTouchHandler {
mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds);
}
final PointF vel = mTouchState.getVelocity();
final float velocity = PointF.length(vel.x, vel.y);
if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds);
@@ -495,29 +510,39 @@ public class PipTouchHandler {
};
/**
* @return whether the gesture ending in the {@param vel} is fast enough to be a fling towards
* the same edge the PIP is on. Used to identify a minimize gesture.
* @return whether the gesture ending in {@param vel} is fast enough to be a fling and towards
* the provided {@param edge} where:
*
* 1 = top
* 2 = left
* 3 = right
* 4 = bottom
*/
private boolean isHorizontalFlingTowardsCurrentEdge(PointF vel) {
final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
final boolean isFling = PointF.length(vel.x, vel.y) > mFlingAnimationUtils
.getMinVelocityPxPerSecond();
final boolean towardsCurrentEdge = isOverEdge(true /* left */) && vel.x < 0
|| isOverEdge(false /* right */) && vel.x > 0;
return towardsCurrentEdge && isHorizontal && isFling;
}
/**
* @return whether the given bounds are on the left or right edge (depending on
* {@param checkLeft})
*/
private boolean isOverEdge(boolean checkLeft) {
private boolean isFlingTowardsEdge(PipTouchState touchState, int edge) {
final PointF vel = touchState.getVelocity();
final PointF downPos = touchState.getDownTouchPosition();
final Rect bounds = mMotionHelper.getBounds();
if (checkLeft) {
return bounds.left <= mMovementBounds.left;
} else {
return bounds.right >= mMovementBounds.right + bounds.width();
final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
final boolean isFling =
PointF.length(vel.x, vel.y) > mFlingAnimationUtils.getMinVelocityPxPerSecond();
if (!isFling) {
return false;
}
switch (edge) {
case 1: // top
return !isHorizontal && vel.y < 0
&& downPos.y <= mMovementBounds.top + bounds.height();
case 2: // left
return isHorizontal && vel.x < 0
&& downPos.x <= mMovementBounds.left + bounds.width();
case 3: // right
return isHorizontal && vel.x > 0
&& downPos.x >= mMovementBounds.right;
case 4: // bottom
return !isHorizontal && vel.y > 0
&& downPos.y >= mMovementBounds.bottom;
}
return false;
}
/**
@@ -542,7 +567,7 @@ public class PipTouchHandler {
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
pw.println(innerPrefix + "mEnableDragToDismiss=" + ENABLE_DRAG_TO_DISMISS);
pw.println(innerPrefix + "mEnableDragToDismiss=" + ENABLE_DISMISS_DRAG_TO_TARGET);
mSnapAlgorithm.dump(pw, innerPrefix);
mTouchState.dump(pw, innerPrefix);
mMotionHelper.dump(pw, innerPrefix);

View File

@@ -169,6 +169,13 @@ public class PipTouchState {
return mLastDelta;
}
/**
* @return the down touch position.
*/
public PointF getDownTouchPosition() {
return mDownTouch;
}
/**
* @return the movement delta between the last handled touch event and the down touch
* position.