Merge changes Id3953904,I2ef286d6 into rvc-dev
* changes: Implement PIP Leash resizing. Track current resized bounds for resized PIP.
This commit is contained in:
@@ -225,8 +225,8 @@ public class PipBoundsHandler {
|
||||
*/
|
||||
Rect getDestinationBounds(float aspectRatio, Rect bounds) {
|
||||
final Rect destinationBounds;
|
||||
final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
|
||||
if (bounds == null) {
|
||||
final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
|
||||
destinationBounds = new Rect(defaultBounds);
|
||||
} else {
|
||||
destinationBounds = new Rect(bounds);
|
||||
|
||||
@@ -18,7 +18,7 @@ package com.android.systemui.pip;
|
||||
|
||||
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
|
||||
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
|
||||
import static com.android.systemui.pip.PipAnimationController.DURATION_NONE;
|
||||
import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -87,19 +87,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
}
|
||||
});
|
||||
final Rect destinationBounds = animator.getDestinationBounds();
|
||||
mLastReportedBounds.set(destinationBounds);
|
||||
try {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
if (animator.shouldScheduleFinishPip()) {
|
||||
wct.scheduleFinishEnterPip(wc, destinationBounds);
|
||||
} else {
|
||||
wct.setBounds(wc, destinationBounds);
|
||||
}
|
||||
wct.setBoundsChangeTransaction(wc, tx);
|
||||
mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to apply container transaction", e);
|
||||
}
|
||||
finishResizeInternal(destinationBounds, wc, tx, animator.shouldScheduleFinishPip());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,15 +112,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
mPipAnimationController = new PipAnimationController(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the PiP window, animate if the given duration is not {@link #DURATION_NONE}
|
||||
*/
|
||||
public void resizePinnedStack(Rect destinationBounds, int durationMs) {
|
||||
Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
|
||||
resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip */,
|
||||
mLastReportedBounds, destinationBounds, durationMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE}
|
||||
*/
|
||||
@@ -143,7 +122,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
}
|
||||
final Rect destinationBounds = new Rect(originalBounds);
|
||||
destinationBounds.offset(xOffset, yOffset);
|
||||
resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip*/,
|
||||
animateResizePipInternal(mTaskInfo.token, false /* scheduleFinishPip*/,
|
||||
originalBounds, destinationBounds, durationMs);
|
||||
}
|
||||
|
||||
@@ -208,15 +187,14 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
mTaskInfo = info;
|
||||
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
|
||||
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
|
||||
resizePinnedStackInternal(mTaskInfo.token, true /* scheduleFinishPip */,
|
||||
currentBounds, destinationBounds,
|
||||
PipAnimationController.DURATION_DEFAULT_MS);
|
||||
animateResizePipInternal(mTaskInfo.token, true /* scheduleFinishPip */,
|
||||
currentBounds, destinationBounds, DURATION_DEFAULT_MS);
|
||||
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||
mMainHandler.post(() -> mPipAnimationController
|
||||
.getAnimator(mTaskInfo.token, true /* scheduleFinishPip */,
|
||||
destinationBounds, 0f, 1f)
|
||||
.setPipAnimationCallback(mPipAnimationCallback)
|
||||
.setDuration(PipAnimationController.DURATION_DEFAULT_MS)
|
||||
.setDuration(DURATION_DEFAULT_MS)
|
||||
.start());
|
||||
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
|
||||
} else {
|
||||
@@ -231,9 +209,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
Log.wtf(TAG, "Unrecognized token: " + token);
|
||||
return;
|
||||
}
|
||||
resizePinnedStackInternal(token, false /* scheduleFinishPip */,
|
||||
mLastReportedBounds, mDisplayBounds,
|
||||
PipAnimationController.DURATION_DEFAULT_MS);
|
||||
animateResizePipInternal(token, false /* scheduleFinishPip */,
|
||||
mLastReportedBounds, mDisplayBounds, DURATION_DEFAULT_MS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -245,11 +222,22 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
|
||||
getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
|
||||
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
|
||||
resizePinnedStack(destinationBounds, PipAnimationController.DURATION_DEFAULT_MS);
|
||||
animateResizePip(destinationBounds, DURATION_DEFAULT_MS);
|
||||
}
|
||||
|
||||
private void resizePinnedStackInternal(IWindowContainer wc, boolean scheduleFinishPip,
|
||||
Rect currentBounds, Rect destinationBounds, int animationDurationMs) {
|
||||
|
||||
/**
|
||||
* Directly perform manipulation/resize on the leash. This will not perform any
|
||||
* {@link WindowContainerTransaction} until {@link #finishResize} is called.
|
||||
*/
|
||||
public void resizePip(Rect destinationBounds) {
|
||||
Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
|
||||
resizePipInternal(mTaskInfo.token, destinationBounds);
|
||||
}
|
||||
|
||||
private void resizePipInternal(IWindowContainer wc,
|
||||
Rect destinationBounds) {
|
||||
Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
|
||||
try {
|
||||
// Could happen when dismissPip
|
||||
if (wc == null || wc.getLeash() == null) {
|
||||
@@ -257,20 +245,10 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
return;
|
||||
}
|
||||
final SurfaceControl leash = wc.getLeash();
|
||||
if (animationDurationMs == DURATION_NONE) {
|
||||
// Directly resize if no animation duration is set. When fling, wait for final
|
||||
// callback to issue the proper WindowContainerTransaction with destination bounds.
|
||||
new SurfaceControl.Transaction()
|
||||
.setPosition(leash, destinationBounds.left, destinationBounds.top)
|
||||
.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
|
||||
.apply();
|
||||
} else {
|
||||
mMainHandler.post(() -> mPipAnimationController
|
||||
.getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds)
|
||||
.setPipAnimationCallback(mPipAnimationCallback)
|
||||
.setDuration(animationDurationMs)
|
||||
.start());
|
||||
}
|
||||
new SurfaceControl.Transaction()
|
||||
.setPosition(leash, destinationBounds.left, destinationBounds.top)
|
||||
.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
|
||||
.apply();
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Abort animation, invalid window container", e);
|
||||
} catch (Exception e) {
|
||||
@@ -278,6 +256,73 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish a intermediate resize operation. This is expected to be called after
|
||||
* {@link #resizePip}.
|
||||
*/
|
||||
public void finishResize(Rect destinationBounds) {
|
||||
try {
|
||||
final IWindowContainer wc = mTaskInfo.token;
|
||||
SurfaceControl.Transaction tx = new SurfaceControl.Transaction()
|
||||
.setPosition(wc.getLeash(), destinationBounds.left,
|
||||
destinationBounds.top)
|
||||
.setWindowCrop(wc.getLeash(), destinationBounds.width(),
|
||||
destinationBounds.height());
|
||||
finishResizeInternal(destinationBounds, wc, tx, false);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to obtain leash");
|
||||
}
|
||||
}
|
||||
|
||||
private void finishResizeInternal(Rect destinationBounds, IWindowContainer wc,
|
||||
SurfaceControl.Transaction tx, boolean shouldScheduleFinishPip) {
|
||||
mLastReportedBounds.set(destinationBounds);
|
||||
try {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
if (shouldScheduleFinishPip) {
|
||||
wct.scheduleFinishEnterPip(wc, destinationBounds);
|
||||
} else {
|
||||
wct.setBounds(wc, destinationBounds);
|
||||
}
|
||||
wct.setBoundsChangeTransaction(mTaskInfo.token, tx);
|
||||
mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to apply container transaction", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates resizing of the pinned stack given the duration.
|
||||
*/
|
||||
public void animateResizePip(Rect destinationBounds, int durationMs) {
|
||||
Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
|
||||
animateResizePipInternal(mTaskInfo.token, false, mLastReportedBounds,
|
||||
destinationBounds, durationMs);
|
||||
}
|
||||
|
||||
private void animateResizePipInternal(IWindowContainer wc, boolean scheduleFinishPip,
|
||||
Rect currentBounds, Rect destinationBounds, int durationMs) {
|
||||
try {
|
||||
// Could happen when dismissPip
|
||||
if (wc == null || wc.getLeash() == null) {
|
||||
Log.w(TAG, "Abort animation, invalid leash");
|
||||
return;
|
||||
}
|
||||
final SurfaceControl leash = wc.getLeash();
|
||||
|
||||
mMainHandler.post(() -> mPipAnimationController
|
||||
.getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds)
|
||||
.setPipAnimationCallback(mPipAnimationCallback)
|
||||
.setDuration(durationMs)
|
||||
.start());
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Abort animation, invalid window container", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Should not reach here, terrible thing happened", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
|
||||
return params == null
|
||||
? mPipBoundsHandler.getDefaultAspectRatio()
|
||||
|
||||
@@ -168,7 +168,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
|
||||
// bounds. We want to restore to the unexpanded bounds when re-entering pip,
|
||||
// so we save the bounds before expansion (normal) instead of the current
|
||||
// bounds.
|
||||
mReentryBounds.set(mTouchHandler.getNormalBounds());
|
||||
mReentryBounds.set(mTouchHandler.getMinBounds());
|
||||
// Apply the snap fraction of the current bounds to the normal bounds.
|
||||
float snapFraction = mPipBoundsHandler.getSnapFraction(bounds);
|
||||
mPipBoundsHandler.applySnapFraction(mReentryBounds, snapFraction);
|
||||
|
||||
@@ -38,7 +38,6 @@ import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.systemui.pip.PipAnimationController;
|
||||
import com.android.systemui.pip.PipSnapAlgorithm;
|
||||
import com.android.systemui.pip.PipTaskOrganizer;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
@@ -546,7 +545,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
case MSG_RESIZE_IMMEDIATE: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
Rect toBounds = (Rect) args.arg1;
|
||||
mPipTaskOrganizer.resizePinnedStack(toBounds, PipAnimationController.DURATION_NONE);
|
||||
mPipTaskOrganizer.resizePip(toBounds);
|
||||
mBounds.set(toBounds);
|
||||
return true;
|
||||
}
|
||||
@@ -564,7 +563,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
return true;
|
||||
}
|
||||
|
||||
mPipTaskOrganizer.resizePinnedStack(toBounds, duration);
|
||||
mPipTaskOrganizer.animateResizePip(toBounds, duration);
|
||||
mBounds.set(toBounds);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.view.MotionEvent;
|
||||
import com.android.internal.policy.TaskResizingAlgorithm;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.pip.PipBoundsHandler;
|
||||
import com.android.systemui.pip.PipTaskOrganizer;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -64,6 +65,7 @@ public class PipResizeGestureHandler {
|
||||
private final PointF mDownPoint = new PointF();
|
||||
private final Point mMaxSize = new Point();
|
||||
private final Point mMinSize = new Point();
|
||||
private final Rect mLastResizeBounds = new Rect();
|
||||
private final Rect mTmpBounds = new Rect();
|
||||
private final int mDelta;
|
||||
|
||||
@@ -74,12 +76,13 @@ public class PipResizeGestureHandler {
|
||||
|
||||
private InputMonitor mInputMonitor;
|
||||
private InputEventReceiver mInputEventReceiver;
|
||||
private PipTaskOrganizer mPipTaskOrganizer;
|
||||
|
||||
private int mCtrlType;
|
||||
|
||||
public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
|
||||
PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper,
|
||||
DeviceConfigProxy deviceConfig) {
|
||||
DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer) {
|
||||
final Resources res = context.getResources();
|
||||
context.getDisplay().getMetrics(mDisplayMetrics);
|
||||
mDisplayId = context.getDisplayId();
|
||||
@@ -87,6 +90,7 @@ public class PipResizeGestureHandler {
|
||||
mPipBoundsHandler = pipBoundsHandler;
|
||||
mPipTouchHandler = pipTouchHandler;
|
||||
mMotionHelper = motionHelper;
|
||||
mPipTaskOrganizer = pipTaskOrganizer;
|
||||
|
||||
context.getDisplay().getRealSize(mMaxSize);
|
||||
mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
|
||||
@@ -190,11 +194,7 @@ public class PipResizeGestureHandler {
|
||||
}
|
||||
|
||||
} else if (mAllowGesture) {
|
||||
final Rect currentPipBounds = mMotionHelper.getBounds();
|
||||
Rect newSize = TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(), mDownPoint.x,
|
||||
mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, mMinSize.y, mMaxSize,
|
||||
true, true);
|
||||
mPipBoundsHandler.transformBoundsToAspectRatio(newSize);
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
// We do not support multi touch for resizing via drag
|
||||
@@ -203,12 +203,18 @@ public class PipResizeGestureHandler {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// Capture inputs
|
||||
mInputMonitor.pilferPointers();
|
||||
//TODO: Actually do resize here.
|
||||
final Rect currentPipBounds = mMotionHelper.getBounds();
|
||||
mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
|
||||
mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
|
||||
mMinSize.y, mMaxSize, true, true));
|
||||
mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
|
||||
mPipTaskOrganizer.resizePip(mLastResizeBounds);
|
||||
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
//TODO: Finish resize operation here.
|
||||
mMotionHelper.synchronizePinnedStackBounds();
|
||||
mPipTaskOrganizer.finishResize(mLastResizeBounds);
|
||||
mLastResizeBounds.setEmpty();
|
||||
mCtrlType = CTRL_NONE;
|
||||
mAllowGesture = false;
|
||||
break;
|
||||
@@ -220,7 +226,7 @@ public class PipResizeGestureHandler {
|
||||
mMaxSize.set(maxX, maxY);
|
||||
}
|
||||
|
||||
void updateMiniSize(int minX, int minY) {
|
||||
void updateMinSize(int minX, int minY) {
|
||||
mMinSize.set(minX, minY);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityWindowInfo;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.logging.MetricsLoggerWrapper;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.pip.PipBoundsHandler;
|
||||
@@ -73,7 +74,7 @@ public class PipTouchHandler {
|
||||
private final Context mContext;
|
||||
private final IActivityManager mActivityManager;
|
||||
private final PipBoundsHandler mPipBoundsHandler;
|
||||
private final PipResizeGestureHandler mPipResizeGestureHandler;
|
||||
private PipResizeGestureHandler mPipResizeGestureHandler;
|
||||
private IPinnedStackController mPinnedStackController;
|
||||
|
||||
private final PipMenuActivityController mMenuController;
|
||||
@@ -84,14 +85,22 @@ public class PipTouchHandler {
|
||||
|
||||
// The current movement bounds
|
||||
private Rect mMovementBounds = new Rect();
|
||||
// The current resized bounds, changed by user resize.
|
||||
// Note that this is not necessarily the same as PipMotionHelper#getBounds, since it's possible
|
||||
// that PIP is currently is in a expanded state (max size) but we still need mResizeBounds to
|
||||
// know what size to restore to once expand animation times out.
|
||||
@VisibleForTesting Rect mResizedBounds = new Rect();
|
||||
|
||||
// The reference inset bounds, used to determine the dismiss fraction
|
||||
private Rect mInsetBounds = new Rect();
|
||||
// The reference bounds used to calculate the normal/expanded target bounds
|
||||
private Rect mNormalBounds = new Rect();
|
||||
private Rect mNormalMovementBounds = new Rect();
|
||||
private Rect mExpandedBounds = new Rect();
|
||||
private Rect mExpandedMovementBounds = new Rect();
|
||||
|
||||
// The reference bounds used to calculate the minimum/maximum target bounds
|
||||
// The bound in which PIP enters is the starting/minimum bound, while the expanded/auto-resized
|
||||
// bound is the maximum bound.
|
||||
private Rect mMinBounds = new Rect();
|
||||
@VisibleForTesting Rect mMinMovementBounds = new Rect();
|
||||
private Rect mMaxBounds = new Rect();
|
||||
@VisibleForTesting Rect mMaxMovementBounds = new Rect();
|
||||
private int mExpandedShortestEdgeSize;
|
||||
|
||||
// Used to workaround an issue where the WM rotation happens before we are notified, allowing
|
||||
@@ -126,7 +135,7 @@ public class PipTouchHandler {
|
||||
private final PipTouchState mTouchState;
|
||||
private final FlingAnimationUtils mFlingAnimationUtils;
|
||||
private final FloatingContentCoordinator mFloatingContentCoordinator;
|
||||
private final PipMotionHelper mMotionHelper;
|
||||
private PipMotionHelper mMotionHelper;
|
||||
private PipTouchGesture mGesture;
|
||||
|
||||
// Temp vars
|
||||
@@ -182,7 +191,7 @@ public class PipTouchHandler {
|
||||
mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
|
||||
mPipResizeGestureHandler =
|
||||
new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper,
|
||||
deviceConfig);
|
||||
deviceConfig, pipTaskOrganizer);
|
||||
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
|
||||
() -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
|
||||
mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
|
||||
@@ -235,14 +244,16 @@ public class PipTouchHandler {
|
||||
|
||||
mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
|
||||
}
|
||||
mResizedBounds.setEmpty();
|
||||
mPipResizeGestureHandler.onActivityUnpinned();
|
||||
}
|
||||
|
||||
public void onPinnedStackAnimationEnded() {
|
||||
// Always synchronize the motion helper bounds once PiP animations finish
|
||||
mMotionHelper.synchronizePinnedStackBounds();
|
||||
mPipResizeGestureHandler.updateMiniSize(mMotionHelper.getBounds().width(),
|
||||
mMotionHelper.getBounds().height());
|
||||
|
||||
updateMovementBounds();
|
||||
mResizedBounds.set(mMinBounds);
|
||||
|
||||
if (mShowPipMenuOnAnimationEnd) {
|
||||
mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
|
||||
@@ -266,7 +277,10 @@ public class PipTouchHandler {
|
||||
mShelfHeight = shelfHeight;
|
||||
}
|
||||
|
||||
public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect curBounds,
|
||||
/**
|
||||
* Update all the cached bounds (movement, min, max, etc.)
|
||||
*/
|
||||
public void onMovementBoundsChanged(Rect insetBounds, Rect minBounds, Rect curBounds,
|
||||
boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
|
||||
final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
|
||||
final boolean fromDisplayRotationChanged = (mDisplayRotation != displayRotation);
|
||||
@@ -275,23 +289,25 @@ public class PipTouchHandler {
|
||||
}
|
||||
|
||||
// Re-calculate the expanded bounds
|
||||
mNormalBounds = normalBounds;
|
||||
Rect normalMovementBounds = new Rect();
|
||||
mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
|
||||
mMinBounds.set(minBounds);
|
||||
Rect minMovementBounds = new Rect();
|
||||
mSnapAlgorithm.getMovementBounds(mMinBounds, insetBounds, minMovementBounds,
|
||||
bottomOffset);
|
||||
|
||||
// Calculate the expanded size
|
||||
float aspectRatio = (float) normalBounds.width() / normalBounds.height();
|
||||
float aspectRatio = (float) minBounds.width() / minBounds.height();
|
||||
Point displaySize = new Point();
|
||||
mContext.getDisplay().getRealSize(displaySize);
|
||||
Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
|
||||
Size maxSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
|
||||
mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
|
||||
mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
|
||||
mPipResizeGestureHandler.updateMaxSize(expandedSize.getWidth(), expandedSize.getHeight());
|
||||
Rect expandedMovementBounds = new Rect();
|
||||
mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
|
||||
mMaxBounds.set(0, 0, maxSize.getWidth(), maxSize.getHeight());
|
||||
Rect maxMovementBounds = new Rect();
|
||||
mSnapAlgorithm.getMovementBounds(mMaxBounds, insetBounds, maxMovementBounds,
|
||||
bottomOffset);
|
||||
|
||||
mPipResizeGestureHandler.updateMinSize(minBounds.width(), minBounds.height());
|
||||
mPipResizeGestureHandler.updateMaxSize(mMaxBounds.width(), mMaxBounds.height());
|
||||
|
||||
// The extra offset does not really affect the movement bounds, but are applied based on the
|
||||
// current state (ime showing, or shelf offset) when we need to actually shift
|
||||
int extraOffset = Math.max(
|
||||
@@ -308,8 +324,8 @@ public class PipTouchHandler {
|
||||
final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP
|
||||
* mContext.getResources().getDisplayMetrics().density;
|
||||
final Rect toMovementBounds = mMenuState == MENU_STATE_FULL && willResizeMenu()
|
||||
? new Rect(expandedMovementBounds)
|
||||
: new Rect(normalMovementBounds);
|
||||
? new Rect(maxMovementBounds)
|
||||
: new Rect(minMovementBounds);
|
||||
final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
|
||||
final int toBottom = toMovementBounds.bottom < toMovementBounds.top
|
||||
? toMovementBounds.bottom
|
||||
@@ -323,17 +339,17 @@ public class PipTouchHandler {
|
||||
|
||||
// Update the movement bounds after doing the calculations based on the old movement bounds
|
||||
// above
|
||||
mNormalMovementBounds = normalMovementBounds;
|
||||
mExpandedMovementBounds = expandedMovementBounds;
|
||||
mMinMovementBounds = minMovementBounds;
|
||||
mMaxMovementBounds = maxMovementBounds;
|
||||
mDisplayRotation = displayRotation;
|
||||
mInsetBounds.set(insetBounds);
|
||||
updateMovementBounds(mMenuState);
|
||||
updateMovementBounds();
|
||||
mMovementBoundsExtraOffsets = extraOffset;
|
||||
|
||||
// If we have a deferred resize, apply it now
|
||||
if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
|
||||
mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
|
||||
mNormalMovementBounds, mMovementBounds, true /* immediate */);
|
||||
mMotionHelper.animateToUnexpandedState(minBounds, mSavedSnapFraction,
|
||||
mMinMovementBounds, mMovementBounds, true /* immediate */);
|
||||
mSavedSnapFraction = -1f;
|
||||
mDeferResizeToNormalBoundsUntilRotation = -1;
|
||||
}
|
||||
@@ -387,7 +403,7 @@ public class PipTouchHandler {
|
||||
case MotionEvent.ACTION_UP: {
|
||||
// Update the movement bounds again if the state has changed since the user started
|
||||
// dragging (ie. when the IME shows)
|
||||
updateMovementBounds(mMenuState);
|
||||
updateMovementBounds();
|
||||
|
||||
if (mGesture.onUp(mTouchState)) {
|
||||
break;
|
||||
@@ -485,11 +501,13 @@ public class PipTouchHandler {
|
||||
if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
|
||||
// Save the current snap fraction and if we do not drag or move the PiP, then
|
||||
// we store back to this snap fraction. Otherwise, we'll reset the snap
|
||||
// fraction and snap to the closest edge
|
||||
Rect expandedBounds = new Rect(mExpandedBounds);
|
||||
// fraction and snap to the closest edge.
|
||||
// Also save the current resized bounds so when the menu disappears, we can restore it.
|
||||
if (resize) {
|
||||
mResizedBounds.set(mMotionHelper.getBounds());
|
||||
Rect expandedBounds = new Rect(mMaxBounds);
|
||||
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
|
||||
mMovementBounds, mExpandedMovementBounds);
|
||||
mMovementBounds, mMaxMovementBounds);
|
||||
}
|
||||
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
|
||||
// Try and restore the PiP to the closest edge, using the saved snap fraction
|
||||
@@ -515,9 +533,9 @@ public class PipTouchHandler {
|
||||
}
|
||||
|
||||
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
|
||||
Rect normalBounds = new Rect(mNormalBounds);
|
||||
Rect normalBounds = new Rect(mResizedBounds);
|
||||
mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
|
||||
mNormalMovementBounds, mMovementBounds, false /* immediate */);
|
||||
mMinMovementBounds, mMovementBounds, false /* immediate */);
|
||||
mSavedSnapFraction = -1f;
|
||||
}
|
||||
} else {
|
||||
@@ -528,7 +546,7 @@ public class PipTouchHandler {
|
||||
}
|
||||
}
|
||||
mMenuState = menuState;
|
||||
updateMovementBounds(menuState);
|
||||
updateMovementBounds();
|
||||
// If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
|
||||
// as well, or it can't handle a11y focus and pip menu can't perform any action.
|
||||
onRegistrationChanged(menuState == MENU_STATE_NONE);
|
||||
@@ -544,11 +562,26 @@ public class PipTouchHandler {
|
||||
return mMotionHelper;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
PipResizeGestureHandler getPipResizeGestureHandler() {
|
||||
return mPipResizeGestureHandler;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setPipResizeGestureHandler(PipResizeGestureHandler pipResizeGestureHandler) {
|
||||
mPipResizeGestureHandler = pipResizeGestureHandler;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setPipMotionHelper(PipMotionHelper pipMotionHelper) {
|
||||
mMotionHelper = pipMotionHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the unexpanded bounds.
|
||||
*/
|
||||
public Rect getNormalBounds() {
|
||||
return mNormalBounds;
|
||||
public Rect getMinBounds() {
|
||||
return mMinBounds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -701,17 +734,17 @@ public class PipTouchHandler {
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the current movement bounds based on whether the menu is currently visible and
|
||||
* resized.
|
||||
* Updates the current movement bounds based on the current PIP size.
|
||||
*/
|
||||
private void updateMovementBounds(int menuState) {
|
||||
boolean isMenuExpanded = menuState == MENU_STATE_FULL;
|
||||
mMovementBounds = isMenuExpanded && willResizeMenu()
|
||||
? mExpandedMovementBounds
|
||||
: mNormalMovementBounds;
|
||||
private void updateMovementBounds() {
|
||||
Rect movementBounds = new Rect();
|
||||
mSnapAlgorithm.getMovementBounds(mMotionHelper.getBounds(), mInsetBounds,
|
||||
movementBounds, mIsImeShowing ? mImeHeight : 0);
|
||||
mMotionHelper.setCurrentMovementBounds(movementBounds);
|
||||
|
||||
boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
|
||||
mPipBoundsHandler.setMinEdgeSize(
|
||||
isMenuExpanded ? mExpandedShortestEdgeSize : 0);
|
||||
mMotionHelper.setCurrentMovementBounds(mMovementBounds);
|
||||
isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -729,18 +762,18 @@ public class PipTouchHandler {
|
||||
if (!mEnableResize) {
|
||||
return false;
|
||||
}
|
||||
return mExpandedBounds.width() != mNormalBounds.width()
|
||||
|| mExpandedBounds.height() != mNormalBounds.height();
|
||||
return mMaxBounds.width() != mMinBounds.width()
|
||||
|| mMaxBounds.height() != mMinBounds.height();
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
final String innerPrefix = prefix + " ";
|
||||
pw.println(prefix + TAG);
|
||||
pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
|
||||
pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
|
||||
pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
|
||||
pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
|
||||
pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
|
||||
pw.println(innerPrefix + "mMinBounds=" + mMinBounds);
|
||||
pw.println(innerPrefix + "mMinMovementBounds=" + mMinMovementBounds);
|
||||
pw.println(innerPrefix + "mMaxBounds=" + mMaxBounds);
|
||||
pw.println(innerPrefix + "mMaxMovementBounds=" + mMaxMovementBounds);
|
||||
pw.println(innerPrefix + "mMenuState=" + mMenuState);
|
||||
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
|
||||
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
|
||||
|
||||
@@ -433,8 +433,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
|
||||
mCurrentPipBounds = mPipBounds;
|
||||
break;
|
||||
}
|
||||
mPipTaskOrganizer.resizePinnedStack(
|
||||
mCurrentPipBounds, PipAnimationController.DURATION_DEFAULT_MS);
|
||||
mPipTaskOrganizer.animateResizePip(mCurrentPipBounds,
|
||||
PipAnimationController.DURATION_DEFAULT_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
package com.android.systemui.pip.phone;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.app.IActivityTaskManager;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.util.Size;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.pip.PipBoundsHandler;
|
||||
import com.android.systemui.pip.PipSnapAlgorithm;
|
||||
import com.android.systemui.pip.PipTaskOrganizer;
|
||||
import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.util.DeviceConfigProxy;
|
||||
import com.android.systemui.util.FloatingContentCoordinator;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Unit tests against {@link PipTouchHandler}, including but not limited to:
|
||||
* - Update movement bounds based on new bounds
|
||||
* - Update movement bounds based on IME/shelf
|
||||
* - Update movement bounds to PipResizeHandler
|
||||
*/
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
public class PipTouchHandlerTest extends SysuiTestCase {
|
||||
private static final int ROUNDING_ERROR_MARGIN = 10;
|
||||
private static final float DEFAULT_ASPECT_RATIO = 1f;
|
||||
private static final Rect EMPTY_CURRENT_BOUNDS = null;
|
||||
|
||||
private PipTouchHandler mPipTouchHandler;
|
||||
private DisplayInfo mDefaultDisplayInfo;
|
||||
|
||||
@Mock
|
||||
private IActivityManager mActivityManager;
|
||||
|
||||
@Mock
|
||||
private IActivityTaskManager mIActivityTaskManager;
|
||||
|
||||
@Mock
|
||||
private PipMenuActivityController mPipMenuActivityController;
|
||||
|
||||
@Mock
|
||||
private InputConsumerController mInputConsumerController;
|
||||
|
||||
@Mock
|
||||
private PipBoundsHandler mPipBoundsHandler;
|
||||
|
||||
@Mock
|
||||
private PipTaskOrganizer mPipTaskOrganizer;
|
||||
|
||||
@Mock
|
||||
private FloatingContentCoordinator mFloatingContentCoordinator;
|
||||
|
||||
@Mock
|
||||
private DeviceConfigProxy mDeviceConfigProxy;
|
||||
|
||||
private PipSnapAlgorithm mPipSnapAlgorithm;
|
||||
private PipMotionHelper mMotionHelper;
|
||||
private PipResizeGestureHandler mPipResizeGestureHandler;
|
||||
|
||||
Rect mInsetBounds;
|
||||
Rect mMinBounds;
|
||||
Rect mCurBounds;
|
||||
boolean mFromImeAdjustment;
|
||||
boolean mFromShelfAdjustment;
|
||||
int mDisplayRotation;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mIActivityTaskManager,
|
||||
mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
|
||||
mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy);
|
||||
mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
|
||||
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
|
||||
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
|
||||
mPipTouchHandler.setPipMotionHelper(mMotionHelper);
|
||||
mPipTouchHandler.setPipResizeGestureHandler(mPipResizeGestureHandler);
|
||||
|
||||
// Assume a display of 1000 x 1000
|
||||
// inset of 10
|
||||
mInsetBounds = new Rect(10, 10, 990, 990);
|
||||
// minBounds of 100x100 bottom right corner
|
||||
mMinBounds = new Rect(890, 890, 990, 990);
|
||||
mCurBounds = new Rect();
|
||||
mFromImeAdjustment = false;
|
||||
mFromShelfAdjustment = false;
|
||||
mDisplayRotation = 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateMovementBounds_minBounds() {
|
||||
Rect expectedMinMovementBounds = new Rect();
|
||||
mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0);
|
||||
|
||||
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
|
||||
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
|
||||
|
||||
assertEquals(expectedMinMovementBounds, mPipTouchHandler.mMinMovementBounds);
|
||||
verify(mPipResizeGestureHandler, times(1))
|
||||
.updateMinSize(mMinBounds.width(), mMinBounds.height());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateMovementBounds_maxBounds() {
|
||||
Point displaySize = new Point();
|
||||
mContext.getDisplay().getRealSize(displaySize);
|
||||
Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1,
|
||||
mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y);
|
||||
Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());
|
||||
Rect expectedMaxMovementBounds = new Rect();
|
||||
mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0);
|
||||
|
||||
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
|
||||
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
|
||||
|
||||
assertEquals(expectedMaxMovementBounds, mPipTouchHandler.mMaxMovementBounds);
|
||||
verify(mPipResizeGestureHandler, times(1))
|
||||
.updateMaxSize(maxBounds.width(), maxBounds.height());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateMovementBounds_withImeAdjustment_movesPip() {
|
||||
mFromImeAdjustment = true;
|
||||
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
|
||||
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
|
||||
|
||||
verify(mMotionHelper, times(1)).animateToOffset(any(), anyInt());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user