diff --git a/packages/SystemUI/res/drawable/pip_dismiss_background.xml b/packages/SystemUI/res/drawable/pip_dismiss_background.xml
index 3a7529686dfb0..8f502318ae142 100644
--- a/packages/SystemUI/res/drawable/pip_dismiss_background.xml
+++ b/packages/SystemUI/res/drawable/pip_dismiss_background.xml
@@ -1,22 +1,22 @@
-
-
+ android:shape="rectangle">
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/pip_dismiss_view.xml b/packages/SystemUI/res/layout/pip_dismiss_view.xml
index 141e610d22944..f02a56cd5f810 100644
--- a/packages/SystemUI/res/layout/pip_dismiss_view.xml
+++ b/packages/SystemUI/res/layout/pip_dismiss_view.xml
@@ -1,5 +1,6 @@
-
-
+ android:alpha="0">
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
index cf65f4aa830bb..0f5ca9bdf2fc5 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -27,7 +27,7 @@
android:layout_height="48dp"
android:layout_gravity="top|end"
android:padding="10dp"
- android:contentDescription="@string/pip_phone_dismiss"
+ android:contentDescription="@string/pip_phone_close"
android:src="@drawable/ic_close_white"
android:background="?android:selectableItemBackgroundBorderless" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d3e965aedc985..0e488655d31d3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1746,8 +1746,8 @@
Minimize
-
- Dismiss
+
+ Close
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index a7ac719149f9a..e1821761ba7ad 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -17,13 +17,19 @@
package com.android.systemui.pip.phone;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.TouchDelegate;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.FrameLayout;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -35,12 +41,22 @@ 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;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -51,25 +67,37 @@ public class PipDismissViewController {
*/
public void createDismissTarget() {
if (mDismissView == null) {
+ // Determine sizes for the gradient
+ Point windowSize = new Point();
+ mWindowManager.getDefaultDisplay().getSize(windowSize);
+ mMinHeight = windowSize.y * DISMISS_GRADIENT_MIN_HEIGHT_PERCENT;
+ mMaxHeight = windowSize.y * DISMISS_GRADIENT_MAX_HEIGHT_PERCENT;
+
// Create a new view for the dismiss target
- int dismissTargetSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.pip_dismiss_target_size);
LayoutInflater inflater = LayoutInflater.from(mContext);
mDismissView = inflater.inflate(R.layout.pip_dismiss_view, null);
- mDismissView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ mGradientView = mDismissView.findViewById(R.id.gradient_view);
+ FrameLayout.LayoutParams glp = (android.widget.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);
+ 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 (mDismissView != null) {
- mDismissView.getBoundsOnScreen(mDismissTargetScreenBounds);
+ if (mDismissContainer != null) {
+ mDismissContainer.getBoundsOnScreen(mDismissTargetScreenBounds);
}
}
});
// Add the target to the window
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- dismissTargetSize,
- dismissTargetSize,
+ windowSize.x,
+ (int) mMaxHeight,
WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
@@ -84,7 +112,8 @@ public class PipDismissViewController {
/**
* Shows the dismiss target.
*/
- public void showDismissTarget() {
+ public void showDismissTarget(Rect pinnedStack) {
+ updateDismissTarget(pinnedStack);
mDismissView.animate()
.alpha(1f)
.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
@@ -114,6 +143,36 @@ public class PipDismissViewController {
}
}
+ /**
+ * 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.
*/
@@ -121,4 +180,10 @@ public class PipDismissViewController {
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;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 30aa03cb2cebb..44572f7e025d9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -33,6 +33,7 @@ import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
@@ -71,6 +72,7 @@ public class PipTouchHandler implements TunerService.Tunable {
private static final int DISMISS_STACK_DURATION = 375;
private static final int EXPAND_STACK_DURATION = 225;
private static final int MINIMIZE_STACK_MAX_DURATION = 200;
+ private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 200;
// 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;
@@ -105,6 +107,16 @@ public class PipTouchHandler implements TunerService.Tunable {
}
};
+ private Handler mHandler = new Handler();
+ private Runnable mShowDismissAffordance = new Runnable() {
+ @Override
+ public void run() {
+ if (mEnableDragToDismiss) {
+ mDismissViewController.showDismissTarget(mPinnedStackBounds);
+ }
+ }
+ };
+
// Behaviour states
private boolean mIsTappingThrough;
private boolean mIsMinimized;
@@ -193,7 +205,7 @@ public class PipTouchHandler implements TunerService.Tunable {
mTouchState = new PipTouchState(mViewConfig);
mFlingAnimationUtils = new FlingAnimationUtils(context, 2f);
mGestures = new PipTouchGesture[] {
- mDragToDismissGesture, mDefaultMovementGesture
+ mDefaultMovementGesture
};
mMotionHelper = new PipMotionHelper(BackgroundThread.getHandler());
registerInputConsumer();
@@ -521,6 +533,9 @@ public class PipTouchHandler implements TunerService.Tunable {
private void movePinnedStack(Rect bounds) {
if (!bounds.equals(mPinnedStackBounds)) {
mPinnedStackBounds.set(bounds);
+ if (mEnableDragToDismiss) {
+ mDismissViewController.updateDismissTarget(bounds);
+ }
mMotionHelper.resizeToBounds(mPinnedStackBounds);
}
}
@@ -562,58 +577,26 @@ public class PipTouchHandler implements TunerService.Tunable {
return PointF.length(r1.left - r2.left, r1.top - r2.top);
}
- /**
- * Gesture controlling dragging over a target to dismiss the PIP.
- */
- private PipTouchGesture mDragToDismissGesture = new PipTouchGesture() {
- @Override
- public void onDown(PipTouchState touchState) {
- if (mEnableDragToDismiss) {
- // TODO: Consider setting a timer such at after X time, we show the dismiss
- // target if the user hasn't already dragged some distance
- mDismissViewController.createDismissTarget();
- }
- }
-
- @Override
- boolean onMove(PipTouchState touchState) {
- if (mEnableDragToDismiss && touchState.startedDragging()) {
- mDismissViewController.showDismissTarget();
- }
- return false;
- }
-
- @Override
- public boolean onUp(PipTouchState touchState) {
- if (mEnableDragToDismiss) {
- try {
- if (touchState.isDragging()) {
- Rect dismissBounds = mDismissViewController.getDismissBounds();
- PointF lastTouch = touchState.getLastTouchPosition();
- if (dismissBounds.contains((int) lastTouch.x, (int) lastTouch.y)) {
- animateDismissPinnedStack(dismissBounds);
- MetricsLogger.action(mContext,
- MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
- METRIC_VALUE_DISMISSED_BY_DRAG);
- return true;
- }
- }
- } finally {
- mDismissViewController.destroyDismissTarget();
- }
- }
- return false;
- }
- };
-
- /**** Gestures ****/
-
/**
* Gesture controlling normal movement of the PIP.
*/
private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
+
+ @Override
+ public void onDown(PipTouchState touchState) {
+ if (mEnableDragToDismiss) {
+ mDismissViewController.createDismissTarget();
+ mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
+ }
+ }
+
@Override
boolean onMove(PipTouchState touchState) {
+ if (touchState.startedDragging() && mEnableDragToDismiss) {
+ mHandler.removeCallbacks(mShowDismissAffordance);
+ mDismissViewController.showDismissTarget(mPinnedStackBounds);
+ }
+
if (touchState.isDragging()) {
// Move the pinned stack freely
PointF lastDelta = touchState.getLastTouchDelta();
@@ -635,6 +618,23 @@ public class PipTouchHandler implements TunerService.Tunable {
@Override
public boolean onUp(PipTouchState touchState) {
+ try {
+ if (mEnableDragToDismiss) {
+ 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(mPinnedStackBounds)) {
+ Rect dismissBounds = mDismissViewController.getDismissBounds();
+ animateDismissPinnedStack(dismissBounds);
+ return true;
+ }
+ }
+ }
+ } finally {
+ mDismissViewController.destroyDismissTarget();
+ }
if (touchState.isDragging()) {
PointF vel = mTouchState.getVelocity();
if (!mIsMinimized && (shouldMinimizedPinnedStack()