Merge "Update PIP dismiss to show scrim + info text at the bottom of the screen" into oc-dev

am: 8565eab156

Change-Id: I6ea47f95853616bb9198e02606d62a57d25e993a
This commit is contained in:
Mady Mellor 2017-03-31 17:35:28 +00:00 committed by android-build-merger
commit 722ec3df34
13 changed files with 88 additions and 208 deletions

Binary file not shown.

After

(image error) Size: 518 B

Binary file not shown.

After

(image error) Size: 450 B

Binary file not shown.

After

(image error) Size: 544 B

Binary file not shown.

After

(image error) Size: 544 B

Binary file not shown.

After

(image error) Size: 556 B

@ -1,22 +0,0 @@
<!--
Copyright (C) 2017 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#B3000000"
android:endColor="#00000000"
android:angle="90"/>
</shape>

@ -15,43 +15,18 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pip_dismiss_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="@dimen/pip_dismiss_gradient_height"
android:background="@drawable/pip_dismiss_scrim"
android:alpha="0">
<!-- The height of the below view needs to be animated from a window
so it needs to be in a container to resize smoothly -->
<View
android:id="@+id/gradient_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:background="@drawable/pip_dismiss_background" />
<LinearLayout
android:id="@+id/pip_dismiss_container"
<TextView
android:id="@+id/pip_dismiss_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:orientation="horizontal"
android:paddingBottom="32dp" >
<ImageView
android:id="@+id/pip_dismiss_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:padding="2dp"
android:src="@drawable/pip_dismiss"
android:tint="#FFFFFFFF" />
<TextView
android:id="@+id/pip_dismiss_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pip_phone_close"
android:textColor="#FFFFFFFF"
android:textSize="16sp" />
</LinearLayout>
android:text="@string/pip_phone_dismiss_hint"
android:textColor="#FFFFFFFF"
android:textSize="14sp" />
</FrameLayout>

@ -730,8 +730,11 @@
loading full resolution screenshots. -->
<dimen name="recents_fast_fling_velocity">600dp</dimen>
<!-- The size of the PIP drag-to-dismiss target. -->
<dimen name="pip_dismiss_target_size">48dp</dimen>
<!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
<dimen name="pip_dismiss_gradient_height">196dp</dimen>
<!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. -->
<dimen name="pip_dismiss_text_bottom_margin">36dp</dimen>
<!-- The shortest-edge size of the expanded PiP. -->
<dimen name="pip_expanded_shortest_edge_size">160dp</dimen>

@ -1861,9 +1861,12 @@
<!-- Label for PIP action to Minimize the PIP [CHAR LIMIT=25] -->
<string name="pip_phone_minimize">Minimize</string>
<!-- Label for PIP the drag to close zone [CHAR LIMIT=NONE]-->
<!-- Label for PIP close button [CHAR LIMIT=NONE]-->
<string name="pip_phone_close">Close</string>
<!-- Label for PIP the drag to dismiss hint [CHAR LIMIT=NONE]-->
<string name="pip_phone_dismiss_hint">Drag down to dismiss</string>
<!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
<string name="pip_menu_title">Picture in picture menu</string>

@ -41,21 +41,9 @@ public class PipDismissViewController {
private static final int SHOW_TARGET_DELAY = 100;
private static final int SHOW_TARGET_DURATION = 200;
private static final float DISMISS_TEXT_MAX_SCALE = 2f;
private static final float DISMISS_GRADIENT_MIN_HEIGHT_PERCENT = 0.33f;
private static final float DISMISS_GRADIENT_MAX_HEIGHT_PERCENT = 0.5f;
private static final float DISMISS_THRESHOLD = 0.55f;
private Context mContext;
private WindowManager mWindowManager;
private View mDismissView;
private Rect mDismissTargetScreenBounds = new Rect();
private View mDismissContainer;
private View mGradientView;
private float mMinHeight;
private float mMaxHeight;
public PipDismissViewController(Context context) {
mContext = context;
@ -67,42 +55,29 @@ public class PipDismissViewController {
*/
public void createDismissTarget() {
if (mDismissView == null) {
// Determine sizes for the gradient
// Determine sizes for the view
final Rect stableInsets = new Rect();
SystemServicesProxy.getInstance(mContext).getStableInsets(stableInsets);
final Point windowSize = new Point();
mWindowManager.getDefaultDisplay().getRealSize(windowSize);
mMinHeight = windowSize.y * DISMISS_GRADIENT_MIN_HEIGHT_PERCENT;
mMaxHeight = windowSize.y * DISMISS_GRADIENT_MAX_HEIGHT_PERCENT;
final int gradientHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.pip_dismiss_gradient_height);
final int bottomMargin = mContext.getResources().getDimensionPixelSize(
R.dimen.pip_dismiss_text_bottom_margin);
// Create a new view for the dismiss target
LayoutInflater inflater = LayoutInflater.from(mContext);
mDismissView = inflater.inflate(R.layout.pip_dismiss_view, null);
mGradientView = mDismissView.findViewById(R.id.gradient_view);
FrameLayout.LayoutParams glp =
(FrameLayout.LayoutParams) mGradientView.getLayoutParams();
glp.height = (int) mMaxHeight;
mGradientView.setLayoutParams(glp);
mGradientView.setPivotY(windowSize.y);
mGradientView.setScaleY(mMaxHeight / mMinHeight); // Set to min height via scaling
mDismissContainer = mDismissView.findViewById(R.id.pip_dismiss_container);
FrameLayout.LayoutParams clp =
(FrameLayout.LayoutParams) mDismissContainer.getLayoutParams();
clp.bottomMargin = stableInsets.bottom;
mDismissContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (mDismissContainer != null) {
mDismissContainer.getBoundsOnScreen(mDismissTargetScreenBounds);
}
}
});
// Adjust bottom margins of the text
View text = mDismissView.findViewById(R.id.pip_dismiss_text);
FrameLayout.LayoutParams tlp = (FrameLayout.LayoutParams) text.getLayoutParams();
tlp.bottomMargin = stableInsets.bottom + bottomMargin;
// Add the target to the window
LayoutParams lp = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, (int) mMaxHeight,
0, windowSize.y - (int) mMaxHeight,
ViewGroup.LayoutParams.MATCH_PARENT, gradientHeight,
0, windowSize.y - gradientHeight,
LayoutParams.TYPE_SYSTEM_DIALOG,
LayoutParams.FLAG_LAYOUT_IN_SCREEN
| LayoutParams.FLAG_LAYOUT_NO_LIMITS
@ -118,8 +93,7 @@ public class PipDismissViewController {
/**
* Shows the dismiss target.
*/
public void showDismissTarget(Rect pinnedStack) {
updateDismissTarget(pinnedStack);
public void showDismissTarget() {
mDismissView.animate()
.alpha(1f)
.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
@ -148,48 +122,4 @@ public class PipDismissViewController {
.start();
}
}
/**
* Updates the appearance of the dismiss target based on how close the PIP is.
*/
public void updateDismissTarget(Rect pinnedStack) {
// As PIP moves over / away from delete target it grows / shrinks
final float scalePercent = calculateDistancePercent(pinnedStack);
final float newScale = 1 + (DISMISS_TEXT_MAX_SCALE - 1) * scalePercent;
final float minGradientScale = mMinHeight / mMaxHeight;
final float newHeight = Math.max(minGradientScale, scalePercent);
mGradientView.setScaleY(newHeight);
mDismissContainer.setScaleX(newScale);
mDismissContainer.setScaleY(newScale);
}
/**
* @return the percentage of distance the PIP is away from the dismiss target point.
*/
private float calculateDistancePercent(Rect pinnedStack) {
final int distance = mDismissTargetScreenBounds.height();
final int textX = mDismissTargetScreenBounds.centerX();
final int textY = mDismissTargetScreenBounds.bottom;
final float pipCurrX = pinnedStack.centerX();
final float pipCurrY = pinnedStack.bottom;
final float currentDistance = PointF.length(pipCurrX - textX, pipCurrY - textY);
if (currentDistance <= distance) {
return 1 - (currentDistance / distance);
}
return 0;
}
/**
* @return the dismiss target screen bounds.
*/
public Rect getDismissBounds() {
return mDismissTargetScreenBounds;
}
/**
* @return whether the PIP is positioned on the dismiss target enough to be dismissed.
*/
public boolean shouldDismiss(Rect pinnedStack) {
return calculateDistancePercent(pinnedStack) >= DISMISS_THRESHOLD;
}
}

@ -81,7 +81,7 @@ public class PipMenuActivity extends Activity {
private static final long MENU_FADE_DURATION = 125;
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
private static final float DISMISS_BACKGROUND_ALPHA = 0.8f;
private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
private static final float DISABLED_ACTION_ALPHA = 0.54f;

@ -67,7 +67,7 @@ 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.3f;
// The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
private static final float DISMISS_OFFSCREEN_FRACTION = 0.15f;
private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;
private Context mContext;
private IActivityManager mActivityManager;
@ -201,7 +201,7 @@ public class PipMotionHelper {
*/
boolean shouldDismissPip() {
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
mContext.getDisplay().getSize(displaySize);
if (mBounds.bottom > displaySize.y) {
float offscreenFraction = (float) (mBounds.bottom - displaySize.y) / mBounds.height();
return offscreenFraction >= DISMISS_OFFSCREEN_FRACTION;
@ -327,12 +327,14 @@ public class PipMotionHelper {
/**
* Animates the dismissal of the PiP off the edge of the screen.
*/
Rect animateDragToEdgeDismiss(Rect pipBounds, AnimatorUpdateListener listener) {
Rect animateDismiss(Rect pipBounds, float velocityX, float velocityY,
AnimatorUpdateListener listener) {
cancelAnimations();
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
final float velocity = PointF.length(velocityX, velocityY);
final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
Point p = getDismissEndPoint(pipBounds, velocityX, velocityY, isFling);
Rect toBounds = new Rect(pipBounds);
toBounds.offset(0, displaySize.y - pipBounds.top);
toBounds.offsetTo(p.x, p.y);
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, DRAG_TO_DISMISS_STACK_DURATION,
FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
@ -341,6 +343,10 @@ public class PipMotionHelper {
dismissPip();
}
});
if (isFling) {
mFlingAnimationUtils.apply(mBoundsAnimator, 0,
distanceBetweenRectOffsets(mBounds, toBounds), velocity);
}
if (listener != null) {
mBoundsAnimator.addUpdateListener(listener);
}
@ -348,28 +354,6 @@ public class PipMotionHelper {
return toBounds;
}
/**
* Animates the dismissal of the PiP over the dismiss target bounds.
*/
Rect animateDragToTargetDismiss(Rect dismissBounds) {
cancelAnimations();
Rect toBounds = new Rect(dismissBounds.centerX(),
dismissBounds.centerY(),
dismissBounds.centerX() + 1,
dismissBounds.centerY() + 1);
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
DRAG_TO_TARGET_DISMISS_STACK_DURATION,
FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dismissPip();
}
});
mBoundsAnimator.start();
return toBounds;
}
/**
* Cancels all existing animations.
*/
@ -438,6 +422,30 @@ public class PipMotionHelper {
}
}
/**
* @return the coordinates the PIP should animate to based on the direction of velocity when
* dismissing.
*/
private Point getDismissEndPoint(Rect pipBounds, float velX, float velY, boolean isFling) {
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
final float bottomBound = displaySize.y + pipBounds.height() * .1f;
if (isFling && velX != 0 && velY != 0) {
// Line is defined by: y = mx + b, m = slope, b = y-intercept
// Find the slope
final float slope = velY / velX;
// Sub in slope and PiP position to solve for y-intercept: b = y - mx
final float yIntercept = pipBounds.top - slope * pipBounds.left;
// Now find the point on this line when y = bottom bound: x = (y - b) / m
final float x = (bottomBound - yIntercept) / slope;
return new Point((int) x, (int) bottomBound);
} else {
// If it wasn't a fling the velocity on 'up' is not reliable for direction of movement,
// just animate downwards.
return new Point(pipBounds.left, (int) bottomBound);
}
}
/**
* @return the distance between points {@param p1} and {@param p2}.
*/

@ -60,7 +60,6 @@ public class PipTouchHandler implements TunerService.Tunable {
private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 200;
// Allow dragging the PIP to a location to close it
private static final boolean ENABLE_DISMISS_DRAG_TO_TARGET = false;
private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true;
private final Context mContext;
@ -88,8 +87,8 @@ public class PipTouchHandler implements TunerService.Tunable {
private Runnable mShowDismissAffordance = new Runnable() {
@Override
public void run() {
if (ENABLE_DISMISS_DRAG_TO_TARGET) {
mDismissViewController.showDismissTarget(mMotionHelper.getBounds());
if (ENABLE_DISMISS_DRAG_TO_EDGE) {
mDismissViewController.showDismissTarget();
}
}
};
@ -197,6 +196,7 @@ public class PipTouchHandler implements TunerService.Tunable {
if (mIsMinimized) {
setMinimizedStateInternal(false);
}
mDismissViewController.destroyDismissTarget();
}
@Override
@ -487,7 +487,7 @@ public class PipTouchHandler implements TunerService.Tunable {
mMenuController.pokeMenu();
}
if (ENABLE_DISMISS_DRAG_TO_TARGET) {
if (ENABLE_DISMISS_DRAG_TO_EDGE) {
mDismissViewController.createDismissTarget();
mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
}
@ -503,9 +503,9 @@ public class PipTouchHandler implements TunerService.Tunable {
mSavedSnapFraction = -1f;
}
if (touchState.startedDragging() && ENABLE_DISMISS_DRAG_TO_TARGET) {
if (touchState.startedDragging() && ENABLE_DISMISS_DRAG_TO_EDGE) {
mHandler.removeCallbacks(mShowDismissAffordance);
mDismissViewController.showDismissTarget(mMotionHelper.getBounds());
mDismissViewController.showDismissTarget();
}
if (touchState.isDragging()) {
@ -526,9 +526,6 @@ public class PipTouchHandler implements TunerService.Tunable {
mTmpBounds.offsetTo((int) left, (int) top);
mMotionHelper.movePip(mTmpBounds);
if (ENABLE_DISMISS_DRAG_TO_TARGET) {
mDismissViewController.updateDismissTarget(mTmpBounds);
}
if (ENABLE_DISMISS_DRAG_TO_EDGE) {
updateDismissFraction();
}
@ -555,21 +552,22 @@ public class PipTouchHandler implements TunerService.Tunable {
return false;
}
if (ENABLE_DISMISS_DRAG_TO_TARGET) {
final PointF vel = touchState.getVelocity();
final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
final float velocity = PointF.length(vel.x, vel.y);
final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
final boolean isFlingToBot = isFling
&& !isHorizontal && mMovementWithinDismiss && vel.y > 0;
if (ENABLE_DISMISS_DRAG_TO_EDGE) {
try {
mHandler.removeCallbacks(mShowDismissAffordance);
PointF vel = mTouchState.getVelocity();
final float velocity = PointF.length(vel.x, vel.y);
if (touchState.isDragging()
&& velocity < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
if (mDismissViewController.shouldDismiss(mMotionHelper.getBounds())) {
Rect dismissBounds = mDismissViewController.getDismissBounds();
mMotionHelper.animateDragToTargetDismiss(dismissBounds);
MetricsLogger.action(mContext,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_DRAG);
return true;
}
if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
mMotionHelper.animateDismiss(mMotionHelper.getBounds(), vel.x,
vel.y, mUpdateScrimListener);
MetricsLogger.action(mContext,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_DRAG);
return true;
}
} finally {
mDismissViewController.destroyDismissTarget();
@ -577,24 +575,9 @@ public class PipTouchHandler implements TunerService.Tunable {
}
if (touchState.isDragging()) {
final PointF vel = touchState.getVelocity();
final float velocity = PointF.length(vel.x, vel.y);
final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
final boolean isFlingToBot = isFling
&& !isHorizontal && mMovementWithinDismiss && vel.y > 0;
final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize
&& (mStartedOnLeft ? vel.x < 0 : vel.x > 0);
if (ENABLE_DISMISS_DRAG_TO_EDGE
&& (mMotionHelper.shouldDismissPip() || isFlingToBot)) {
mMotionHelper.animateDragToEdgeDismiss(mMotionHelper.getBounds(),
mUpdateScrimListener);
MetricsLogger.action(mContext,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_DRAG);
return true;
} else if (mEnableMinimize &&
if (mEnableMinimize &&
!mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) {
// Pip should be minimized
setMinimizedStateInternal(true);
@ -664,7 +647,7 @@ public class PipTouchHandler implements TunerService.Tunable {
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
pw.println(innerPrefix + "mEnableDragToDismiss=" + ENABLE_DISMISS_DRAG_TO_TARGET);
pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + ENABLE_DISMISS_DRAG_TO_EDGE);
pw.println(innerPrefix + "mEnableMinimize=" + mEnableMinimize);
mSnapAlgorithm.dump(pw, innerPrefix);
mTouchState.dump(pw, innerPrefix);