summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ben Lin <linben@google.com> 2019-12-20 10:56:45 -0800
committer Ben Lin <linben@google.com> 2020-02-10 15:20:38 -0800
commit04c83f6d87cfa900e24cd6efbbf2d93da8697ee3 (patch)
tree0445f7ef37ec4a06e574f7cfc9b57b5e141b098b
parent6c0f623a01ef69474f86819d5438116f375e4d14 (diff)
Implement stretch-free resizing for PIP (input side).
This is a feature of two components: 1) Input code that takes user input around the PIP window border and starts resizing 2) The actual resizing of the PIP task/stack that currently lives in WM, but will live in SystemUI once migration is done to using Task Organizer The CL only takes care of #1, the input side living in SystemUI. Bug: 111426176 Test: Manually drag the corner of PIP window Change-Id: If4b5e6ea5c482f4fa6eb571b441cf9e6966fbf4b
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java5
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java235
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java1
6 files changed, 260 insertions, 0 deletions
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 3322834c9ed1..086b9d83fed5 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -377,6 +377,11 @@ public final class SystemUiDeviceConfigFlags {
public static final String NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN =
"nav_bar_handle_show_over_lockscreen";
+ /**
+ * (boolean) Whether to enable user-drag resizing for PIP.
+ */
+ public static final String PIP_USER_RESIZE = "pip_user_resize";
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0db7d67efd8d..f0e4e53dfc53 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -977,6 +977,9 @@
Equal to pip_action_size - pip_action_padding. -->
<dimen name="pip_expand_container_edge_margin">30dp</dimen>
+ <!-- The touchable/draggable edge size for PIP resize. -->
+ <dimen name="pip_resize_edge_size">30dp</dimen>
+
<dimen name="default_gear_space">18dp</dimen>
<dimen name="cell_overlay_padding">18dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 6f03f18ef64b..41b31306a931 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -345,6 +345,14 @@ public class PipBoundsHandler {
}
/**
+ * Sets the current bound with the currently store aspect ratio.
+ * @param stackBounds
+ */
+ public void transformBoundsToAspectRatio(Rect stackBounds) {
+ transformBoundsToAspectRatio(stackBounds, mAspectRatio, true);
+ }
+
+ /**
* Set the current bounds (or the default bounds if there are no current bounds) with the
* specified aspect ratio.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
new file mode 100644
index 000000000000..9fb623471bf7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -0,0 +1,235 @@
+/*
+ * 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 com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_USER_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;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.hardware.input.InputManager;
+import android.os.Looper;
+import android.provider.DeviceConfig;
+import android.util.DisplayMetrics;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.InputMonitor;
+import android.view.MotionEvent;
+
+import com.android.internal.policy.TaskResizingAlgorithm;
+import com.android.systemui.R;
+import com.android.systemui.pip.PipBoundsHandler;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
+ * trigger dynamic resize.
+ */
+public class PipResizeGestureHandler {
+
+ private static final String TAG = "PipResizeGestureHandler";
+
+ private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private final PipBoundsHandler mPipBoundsHandler;
+ private final PipTouchHandler mPipTouchHandler;
+ private final PipMotionHelper mMotionHelper;
+ private final int mDisplayId;
+ private final Executor mMainExecutor;
+ private final Region mTmpRegion = new Region();
+
+ private final PointF mDownPoint = new PointF();
+ private final Point mMaxSize = new Point();
+ private final Point mMinSize = new Point();
+ private final Rect mTmpBounds = new Rect();
+ private final int mDelta;
+
+ private boolean mAllowGesture = false;
+ private boolean mIsAttached;
+ private boolean mIsEnabled;
+ private boolean mEnablePipResize;
+
+ private InputMonitor mInputMonitor;
+ private InputEventReceiver mInputEventReceiver;
+
+ private int mCtrlType;
+
+ public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
+ PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper) {
+ final Resources res = context.getResources();
+ context.getDisplay().getMetrics(mDisplayMetrics);
+ mDisplayId = context.getDisplayId();
+ mMainExecutor = context.getMainExecutor();
+ mPipBoundsHandler = pipBoundsHandler;
+ mPipTouchHandler = pipTouchHandler;
+ mMotionHelper = motionHelper;
+
+ context.getDisplay().getRealSize(mMaxSize);
+ mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
+
+ mEnablePipResize = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ PIP_USER_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)) {
+ mEnablePipResize = properties.getBoolean(
+ PIP_USER_RESIZE, /* defaultValue = */ false);
+ }
+ }
+ });
+ }
+
+ private void disposeInputChannel() {
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
+ }
+ if (mInputMonitor != null) {
+ mInputMonitor.dispose();
+ mInputMonitor = null;
+ }
+ }
+
+ void onActivityPinned() {
+ mIsAttached = true;
+ updateIsEnabled();
+ }
+
+ void onActivityUnpinned() {
+ mIsAttached = false;
+ updateIsEnabled();
+ }
+
+ private void updateIsEnabled() {
+ boolean isEnabled = mIsAttached && mEnablePipResize;
+ if (isEnabled == mIsEnabled) {
+ return;
+ }
+ mIsEnabled = isEnabled;
+ disposeInputChannel();
+
+ if (mIsEnabled) {
+ // Register input event receiver
+ mInputMonitor = InputManager.getInstance().monitorGestureInput(
+ "pip-resize", mDisplayId);
+ mInputEventReceiver = new SysUiInputEventReceiver(
+ mInputMonitor.getInputChannel(), Looper.getMainLooper());
+ }
+ }
+
+ private void onInputEvent(InputEvent ev) {
+ if (ev instanceof MotionEvent) {
+ onMotionEvent((MotionEvent) ev);
+ }
+ }
+
+ private boolean isWithinTouchRegion(int x, int y) {
+ final Rect currentPipBounds = mMotionHelper.getBounds();
+ if (currentPipBounds == null) {
+ return false;
+ }
+
+ mTmpBounds.set(currentPipBounds);
+ mTmpBounds.inset(-mDelta, -mDelta);
+
+ mTmpRegion.set(mTmpBounds);
+ mTmpRegion.op(currentPipBounds, Region.Op.DIFFERENCE);
+
+ if (mTmpRegion.contains(x, y)) {
+ if (x < currentPipBounds.left) {
+ mCtrlType |= CTRL_LEFT;
+ }
+ if (x > currentPipBounds.right) {
+ mCtrlType |= CTRL_RIGHT;
+ }
+ if (y < currentPipBounds.top) {
+ mCtrlType |= CTRL_TOP;
+ }
+ if (y > currentPipBounds.bottom) {
+ mCtrlType |= CTRL_BOTTOM;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void onMotionEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
+ if (mAllowGesture) {
+ mDownPoint.set(ev.getX(), ev.getY());
+ }
+
+ } 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
+ mAllowGesture = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // Capture inputs
+ mInputMonitor.pilferPointers();
+ //TODO: Actually do resize here.
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ //TODO: Finish resize operation here.
+ mMotionHelper.synchronizePinnedStackBounds();
+ mCtrlType = CTRL_NONE;
+ mAllowGesture = false;
+ break;
+ }
+ }
+ }
+
+ void updateMaxSize(int maxX, int maxY) {
+ mMaxSize.set(maxX, maxY);
+ }
+
+ void updateMiniSize(int minX, int minY) {
+ mMinSize.set(minX, minY);
+ }
+
+ class SysUiInputEventReceiver extends InputEventReceiver {
+ SysUiInputEventReceiver(InputChannel channel, Looper looper) {
+ super(channel, looper);
+ }
+
+ public void onInputEvent(InputEvent event) {
+ PipResizeGestureHandler.this.onInputEvent(event);
+ finishInputEvent(event, true);
+ }
+ }
+}
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 65cc666d5164..924edb6fe312 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -73,6 +73,7 @@ public class PipTouchHandler {
private final ViewConfiguration mViewConfig;
private final PipMenuListener mMenuListener = new PipMenuListener();
private final PipBoundsHandler mPipBoundsHandler;
+ private final PipResizeGestureHandler mPipResizeGestureHandler;
private IPinnedStackController mPinnedStackController;
private final PipMenuActivityController mMenuController;
@@ -188,6 +189,8 @@ public class PipTouchHandler {
mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
+ mPipResizeGestureHandler =
+ new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper);
mTouchState = new PipTouchState(mViewConfig, mHandler,
() -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
@@ -227,6 +230,7 @@ public class PipTouchHandler {
public void onActivityPinned() {
cleanUp();
mShowPipMenuOnAnimationEnd = true;
+ mPipResizeGestureHandler.onActivityPinned();
}
public void onActivityUnpinned(ComponentName topPipActivity) {
@@ -234,11 +238,14 @@ public class PipTouchHandler {
// Clean up state after the last PiP activity is removed
cleanUp();
}
+ 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());
if (mShowPipMenuOnAnimationEnd) {
mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
@@ -279,6 +286,7 @@ public class PipTouchHandler {
Size expandedSize = 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,
bottomOffset);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 44a320419309..745843deeddb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -375,6 +375,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
mDownPoint.set(ev.getX(), ev.getY());
mThresholdCrossed = false;
}
+
} else if (mAllowGesture) {
if (!mThresholdCrossed) {
if (action == MotionEvent.ACTION_POINTER_DOWN) {