package com.android.internal.widget;
+import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
* After creating the view, the function {@link #setPhoneWindow} needs to be called to make
* the connection to it's owning PhoneWindow.
* Note: At this time the application can change various attributes of the DecorView which
- * will break things (in settle/unexpected ways):
+ * will break things (in subtle/unexpected ways):
* <ul>
* <li>setOutlineProvider</li>
* <li>setSurfaceFormat</li>
// True if the window is being dragged.
private boolean mDragging = false;
- // True when the left mouse button got released while dragging.
- private boolean mLeftMouseButtonReleased;
-
private boolean mOverlayWithAppContent = false;
private View mCaption;
private View mContent;
+ private View mPip;
+ private View mMinimize;
private View mMaximize;
private View mClose;
private GestureDetector mGestureDetector;
private final Rect mCloseRect = new Rect();
private final Rect mMaximizeRect = new Rect();
+ private final Rect mMinimizeRect = new Rect();
+ private final Rect mPipRect = new Rect();
private View mClickTarget;
+ // region @boringdroid
+ private View mBack;
+ private final Rect mBackRect = new Rect();
+ // endregion
public DecorCaptionView(Context context) {
super(context);
// By changing the outline provider to BOUNDS, the window can remove its
// background without removing the shadow.
mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
+ // region @boringdroid
+ mBack = findViewById(R.id.back_window);
+ // endregion
+ mPip = findViewById(R.id.pip_window);
+ mMinimize = findViewById(R.id.minimize_window);
mMaximize = findViewById(R.id.maximize_window);
mClose = findViewById(R.id.close_window);
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
final int x = (int) ev.getX();
final int y = (int) ev.getY();
+ // region @boringdroid
+ if (mBackRect.contains(x, y)) {
+ mClickTarget = mBack;
+ }
+ // endregion
+ // Only offset y for containment tests because the actual views are already translated.
+ if (mPipRect.contains(x, y)) {
+ mClickTarget = mPip;
+ }
+ if (mMinimizeRect.contains(x, y)) {
+ mClickTarget = mMinimize;
+ }
if (mMaximizeRect.contains(x, y)) {
mClickTarget = mMaximize;
}
// input device we are listening to.
final int x = (int) e.getX();
final int y = (int) e.getY();
+ final boolean fromMouse = e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE;
+ final boolean primaryButton = (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0;
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
if (!mShow) {
}
// Checking for a drag action is started if we aren't dragging already and the
// starting event is either a left mouse button or any other input device.
- if (((e.getToolType(e.getActionIndex()) != MotionEvent.TOOL_TYPE_MOUSE ||
- (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0))) {
+ if (!fromMouse || primaryButton) {
mCheckForDragging = true;
mTouchDownX = x;
mTouchDownY = y;
break;
case MotionEvent.ACTION_MOVE:
- if (!mDragging && mCheckForDragging && passedSlop(x, y)) {
+ if (!mDragging && mCheckForDragging && (fromMouse || passedSlop(x, y))) {
mCheckForDragging = false;
mDragging = true;
- mLeftMouseButtonReleased = false;
startMovingTask(e.getRawX(), e.getRawY());
- } else if (mDragging && !mLeftMouseButtonReleased) {
- if (e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE &&
- (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) == 0) {
- // There is no separate mouse button up call and if the user mixes mouse
- // button drag actions, we stop dragging once he releases the button.
- mLeftMouseButtonReleased = true;
- break;
- }
+ // After the above call the framework will take over the input.
+ // This handler will receive ACTION_CANCEL soon (possible after a few spurious
+ // ACTION_MOVE events which are safe to ignore).
}
break;
if (mCaption.getVisibility() != View.GONE) {
mCaption.layout(0, 0, mCaption.getMeasuredWidth(), mCaption.getMeasuredHeight());
captionHeight = mCaption.getBottom() - mCaption.getTop();
+ // region @boringdroid
+ mBack.getHitRect(mBackRect);
+ // endregion
+ mPip.getHitRect(mPipRect);
+ mMinimize.getHitRect(mMinimizeRect);
mMaximize.getHitRect(mMaximizeRect);
mClose.getHitRect(mCloseRect);
} else {
captionHeight = 0;
+ // region @boringdroid
+ mBackRect.setEmpty();
+ // endregion
+ mPipRect.setEmpty();
+ mMinimizeRect.setEmpty();
mMaximizeRect.setEmpty();
mCloseRect.setEmpty();
}
}
// This assumes that the caption bar is at the top.
- mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
+ // region @boringdroid
+ // mOwner.notifyRestrictedCaptionAreaCallback(mPip.getLeft(), mMaximize.getTop(),
+ // mClose.getRight(), mClose.getBottom());
+ mOwner.notifyRestrictedCaptionAreaCallback(mBack.getLeft(), mBack.getTop(),
mClose.getRight(), mClose.getBottom());
+ // endregion
}
/**
* Determine if the workspace is entirely covered by the window.
}
}
+ private void minimizeWindow() {
+ Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
+ if (callback != null) {
+ callback.moveTaskToBack(true);
+ }
+ }
+
+ private void pipWindow() {
+ Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
+ if (callback != null) {
+ callback.enterPictureInPictureModeIfPossible(); /* Send the task to PIP mode if the task supports it. */
+ }
+ }
+
public boolean isCaptionShowing() {
return mShow;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
- if (mClickTarget == mMaximize) {
+ // region @boringdroid
+ if (mClickTarget == mBack) {
+ Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
+ if (callback instanceof Activity) {
+ Activity activity = (Activity) callback;
+ activity.onBackPressed();
+ }
+ return true;
+ }
+ // endregion
+ if (mClickTarget == mMinimize) {
+ minimizeWindow();
+ } else if (mClickTarget == mPip) {
+ pipWindow();
+ } else if (mClickTarget == mMaximize) {
maximizeWindow();
} else if (mClickTarget == mClose) {
mOwner.dispatchOnWindowDismissed(