Merge "Implement stretch-free resizing for PIP (input side)."

This commit is contained in:
Ben Lin
2020-02-11 05:40:29 +00:00
committed by Android (Google) Code Review
6 changed files with 260 additions and 0 deletions

View File

@@ -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() {
}
}

View File

@@ -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>

View File

@@ -344,6 +344,14 @@ public class PipBoundsHandler {
&& Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
}
/**
* 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.

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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) {