Implement pinch to resize.

Bug: 166478885
Test: adb shell cmd device_config put systemui pip_pinch_resize true
Change-Id: Ide6fecd7a26c040b533fb49d399744c5617bfc93
This commit is contained in:
Ben Lin
2020-09-02 12:56:12 -07:00
parent 932d71679f
commit 7c10e48b42
3 changed files with 146 additions and 33 deletions

View File

@@ -390,9 +390,9 @@ public final class SystemUiDeviceConfigFlags {
public static final String CHOOSER_TARGET_RANKING_ENABLED = "chooser_target_ranking_enabled";
/**
* (boolean) Whether to enable user-drag resizing for PIP.
* (boolean) Whether to enable pinch resizing for PIP.
*/
public static final String PIP_USER_RESIZE = "pip_user_resize";
public static final String PIP_PINCH_RESIZE = "pip_pinch_resize";
/**
* (float) Bottom height in DP for Back Gesture.

View File

@@ -15,7 +15,7 @@
*/
package com.android.systemui.pip.phone;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_USER_RESIZE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_PINCH_RESIZE;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
@@ -46,6 +46,7 @@ import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputMonitor;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ViewConfiguration;
import com.android.internal.policy.TaskResizingAlgorithm;
@@ -67,6 +68,8 @@ import java.util.function.Function;
public class PipResizeGestureHandler {
private static final String TAG = "PipResizeGestureHandler";
private static final float PINCH_THRESHOLD = 0.05f;
private static final float STARTING_SCALE_FACTOR = 1.0f;
private static final int INVALID_SYSUI_STATE_MASK =
SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
@@ -83,6 +86,7 @@ public class PipResizeGestureHandler {
private final int mDisplayId;
private final Executor mMainExecutor;
private final SysUiState mSysUiState;
private final ScaleGestureDetector mScaleGestureDetector;
private final Region mTmpRegion = new Region();
private final PointF mDownPoint = new PointF();
@@ -105,8 +109,10 @@ public class PipResizeGestureHandler {
private boolean mAllowGesture;
private boolean mIsAttached;
private boolean mIsEnabled;
private boolean mEnableUserResize;
private boolean mEnablePinchResize;
private boolean mThresholdCrossed;
private boolean mUsingPinchToZoom = false;
private float mScaleFactor = STARTING_SCALE_FACTOR;
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
@@ -136,17 +142,73 @@ public class PipResizeGestureHandler {
context.getDisplay().getRealSize(mMaxSize);
reloadResources();
mEnableUserResize = DeviceConfig.getBoolean(
mScaleGestureDetector = new ScaleGestureDetector(context,
new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
if (!mThresholdCrossed
&& (mScaleFactor > (STARTING_SCALE_FACTOR + PINCH_THRESHOLD)
|| mScaleFactor < (STARTING_SCALE_FACTOR - PINCH_THRESHOLD))) {
mThresholdCrossed = true;
mInputMonitor.pilferPointers();
}
if (mThresholdCrossed) {
int height = Math.min(mMaxSize.y, Math.max(mMinSize.y,
(int) (mScaleFactor * mLastDownBounds.height())));
int width = Math.min(mMaxSize.x, Math.max(mMinSize.x,
(int) (mScaleFactor * mLastDownBounds.width())));
int top, bottom, left, right;
if ((mCtrlType & CTRL_TOP) != 0) {
top = mLastDownBounds.bottom - height;
bottom = mLastDownBounds.bottom;
} else {
top = mLastDownBounds.top;
bottom = mLastDownBounds.top + height;
}
if ((mCtrlType & CTRL_LEFT) != 0) {
left = mLastDownBounds.right - width;
right = mLastDownBounds.right;
} else {
left = mLastDownBounds.left;
right = mLastDownBounds.left + width;
}
mLastResizeBounds.set(left, top, right, bottom);
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds,
mLastResizeBounds,
null);
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
setCtrlTypeForPinchToZoom();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
mScaleFactor = STARTING_SCALE_FACTOR;
finishResize();
}
});
mEnablePinchResize = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_USER_RESIZE,
/* defaultValue = */ true);
PIP_PINCH_RESIZE,
/* defaultValue = */ false);
deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
mEnableUserResize = properties.getBoolean(
PIP_USER_RESIZE, /* defaultValue = */ true);
if (properties.getKeyset().contains(PIP_PINCH_RESIZE)) {
mEnablePinchResize = properties.getBoolean(
PIP_PINCH_RESIZE, /* defaultValue = */ false);
}
}
});
@@ -193,7 +255,7 @@ public class PipResizeGestureHandler {
}
private void updateIsEnabled() {
boolean isEnabled = mIsAttached && mEnableUserResize;
boolean isEnabled = mIsAttached;
if (isEnabled == mIsEnabled) {
return;
}
@@ -211,7 +273,11 @@ public class PipResizeGestureHandler {
private void onInputEvent(InputEvent ev) {
if (ev instanceof MotionEvent) {
onMotionEvent((MotionEvent) ev);
if (mUsingPinchToZoom) {
mScaleGestureDetector.onTouchEvent((MotionEvent) ev);
} else {
onDragCornerResize((MotionEvent) ev);
}
}
}
@@ -254,8 +320,51 @@ public class PipResizeGestureHandler {
}
public boolean willStartResizeGesture(MotionEvent ev) {
return mEnableUserResize && isInValidSysUiState()
&& isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY());
if (isInValidSysUiState()) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// Always pass the DOWN event to the ScaleGestureDetector
mScaleGestureDetector.onTouchEvent(ev);
if (isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) {
return true;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (mEnablePinchResize && ev.getPointerCount() == 2) {
mUsingPinchToZoom = true;
return true;
}
break;
default:
break;
}
}
return false;
}
private void setCtrlTypeForPinchToZoom() {
final Rect currentPipBounds = mMotionHelper.getBounds();
mLastDownBounds.set(mMotionHelper.getBounds());
Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
mDisplayBounds.set(movementBounds.left,
movementBounds.top,
movementBounds.right + currentPipBounds.width(),
movementBounds.bottom + currentPipBounds.height());
if (currentPipBounds.left == mDisplayBounds.left) {
mCtrlType |= CTRL_RIGHT;
} else {
mCtrlType |= CTRL_LEFT;
}
if (currentPipBounds.top > mDisplayBounds.top + mDisplayBounds.height()) {
mCtrlType |= CTRL_TOP;
} else {
mCtrlType |= CTRL_BOTTOM;
}
}
private void setCtrlType(int x, int y) {
@@ -295,7 +404,7 @@ public class PipResizeGestureHandler {
return (mSysUiState.getFlags() & INVALID_SYSUI_STATE_MASK) == 0;
}
private void onMotionEvent(MotionEvent ev) {
private void onDragCornerResize(MotionEvent ev) {
int action = ev.getActionMasked();
float x = ev.getX();
float y = ev.getY();
@@ -345,28 +454,33 @@ public class PipResizeGestureHandler {
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (!mLastResizeBounds.isEmpty()) {
mUserResizeBounds.set(mLastResizeBounds);
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
(Rect bounds) -> {
new Handler(Looper.getMainLooper()).post(() -> {
mMotionHelper.synchronizePinnedStackBounds();
mUpdateMovementBoundsRunnable.run();
resetState();
});
});
mPipUiEventLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
} else {
resetState();
}
finishResize();
break;
}
}
}
private void finishResize() {
if (!mLastResizeBounds.isEmpty()) {
mUserResizeBounds.set(mLastResizeBounds);
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
(Rect bounds) -> {
new Handler(Looper.getMainLooper()).post(() -> {
mMotionHelper.synchronizePinnedStackBounds();
mUpdateMovementBoundsRunnable.run();
resetState();
});
});
mPipUiEventLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
} else {
resetState();
}
}
private void resetState() {
mCtrlType = CTRL_NONE;
mUsingPinchToZoom = false;
mAllowGesture = false;
mThresholdCrossed = false;
}
@@ -397,7 +511,7 @@ public class PipResizeGestureHandler {
pw.println(innerPrefix + "mAllowGesture=" + mAllowGesture);
pw.println(innerPrefix + "mIsAttached=" + mIsAttached);
pw.println(innerPrefix + "mIsEnabled=" + mIsEnabled);
pw.println(innerPrefix + "mEnableUserResize=" + mEnableUserResize);
pw.println(innerPrefix + "mEnablePinchResize=" + mEnablePinchResize);
pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
}

View File

@@ -650,8 +650,7 @@ public class PipTouchHandler {
}
MotionEvent ev = (MotionEvent) inputEvent;
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN
&& mPipResizeGestureHandler.willStartResizeGesture(ev)) {
if (mPipResizeGestureHandler.willStartResizeGesture(ev)) {
// Initialize the touch state for the gesture, but immediately reset to invalidate the
// gesture
mTouchState.onTouchEvent(ev);