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()