Adding user resizability by dragging shadow outsets
This needs to land after 757372.
Bug: 21738872
Change-Id: I69283c8a38d625157dcb1b0d939bfaaa0f140f0c
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 55e23b1..791387d 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -174,6 +174,9 @@
// buttons. The visibility of this decor depends on the workspace and the window type.
// If the window type does not require such a view, this member might be null.
NonClientDecorView mNonClientDecorView;
+
+ private boolean mForwardEvents = false;
+
// The non client decor needs to adapt to the used workspace. Since querying and changing the
// workspace is expensive, this is the workspace value the window is currently set up for.
int mWorkspaceId;
@@ -2495,6 +2498,10 @@
return onInterceptTouchEvent(event);
}
+ private boolean isOutOfInnerBounds(int x, int y) {
+ return x < 0 || y < 0 || x > getWidth() || y > getHeight();
+ }
+
private boolean isOutOfBounds(int x, int y) {
return x < -5 || y < -5 || x > (getWidth() + 5)
|| y > (getHeight() + 5);
@@ -2503,6 +2510,25 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
+ // Dispatch the event to the non client decor if the window is resizable and
+ // the event was (starting) outside the window.
+ if (mHasNonClientDecor && mNonClientDecorView.mResizable) {
+ if (mForwardEvents) {
+ // The non client decor is currently processing the (resize) events.
+ mForwardEvents = mNonClientDecorView.onTouchEvent(event);
+ return true;
+ }
+ if (action == MotionEvent.ACTION_DOWN) {
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ if (isOutOfInnerBounds(x, y)) {
+ // Forward this event to the non client decor.
+ mForwardEvents = mNonClientDecorView.onTouchEvent(event);
+ return mForwardEvents;
+ }
+ }
+ }
+
if (mFeatureId >= 0) {
if (action == MotionEvent.ACTION_DOWN) {
int x = (int)event.getX();
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 372d934..0e22553 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import android.app.ActivityThread;
import android.content.Context;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -26,7 +27,6 @@
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.Window;
-import android.view.WindowInsets;
import android.util.Log;
import android.util.TypedValue;
@@ -79,9 +79,26 @@
// True when the left mouse button got released while dragging.
private boolean mLeftMouseButtonReleased;
+ private static final int NONE = 0;
+ private static final int LEFT = 1;
+ private static final int RIGHT = 2;
+ private static final int TOP = 4;
+ private static final int BOTTOM = 8;
+ private static final int TOP_LEFT = TOP | LEFT;
+ private static final int TOP_RIGHT = TOP | RIGHT;
+ private static final int BOTTOM_LEFT = BOTTOM | LEFT;
+ private static final int BOTTOM_RIGHT = BOTTOM | RIGHT;
+ private int mSizeCorner = NONE;
+
// Avoiding re-creation of Rect's by keeping a temporary window drag bound.
private final Rect mWindowDragBounds = new Rect();
+ // True while the task is resizing itself to avoid overlapping resize operations.
+ private boolean mTaskResizingInProgress = false;
+
+ // True if this window is resizable (which is currently only true when the decor is shown).
+ public boolean mResizable = false;
+
// The current focus state of the window for updating the window elevation.
private boolean mWindowHasFocus = true;
@@ -138,6 +155,15 @@
mLeftMouseButtonReleased = false;
mStartDragX = e.getRawX();
mStartDragY = e.getRawY();
+ // Determine if this is a resizing user action.
+ final int x = (int) (e.getX());
+ final int y = (int) (e.getY());
+ mSizeCorner = (x < 0 ? LEFT : (x >= getWidth() ? RIGHT : NONE)) |
+ (y < 0 ? TOP : (y >= getHeight() ? BOTTOM : NONE));
+ if (mSizeCorner != 0) {
+ // Suppress any configuration changes for now.
+ ActivityThread.currentActivityThread().suppressConfigurationChanges(true);
+ }
}
break;
@@ -150,29 +176,80 @@
mLeftMouseButtonReleased = true;
break;
}
- mWindowDragBounds.set(mWindowOriginalBounds);
- mWindowDragBounds.offset(Math.round(e.getRawX() - mStartDragX),
- Math.round(e.getRawY() - mStartDragY));
- setActivityBounds(mWindowDragBounds);
+ if (mSizeCorner != NONE) {
+ // Avoid overlapping resizing operations.
+ if (mTaskResizingInProgress) {
+ break;
+ }
+ mTaskResizingInProgress = true;
+ // This is a resizing operation.
+ final int deltaX = Math.round(e.getRawX() - mStartDragX);
+ final int deltaY = Math.round(e.getRawY() - mStartDragY);
+ final int minSizeX = (int)(dipToPx(96));
+ final int minSizeY = (int)(dipToPx(64));
+ int left = mWindowOriginalBounds.left;
+ int top = mWindowOriginalBounds.top;
+ int right = mWindowOriginalBounds.right;
+ int bottom = mWindowOriginalBounds.bottom;
+ if ((mSizeCorner & LEFT) != 0) {
+ left = Math.min(left + deltaX, right - minSizeX);
+ }
+ if ((mSizeCorner & TOP) != 0) {
+ top = Math.min(top + deltaY, bottom - minSizeY);
+ }
+ if ((mSizeCorner & RIGHT) != 0) {
+ right = Math.max(left + minSizeX, right + deltaX);
+ }
+ if ((mSizeCorner & BOTTOM) != 0) {
+ bottom = Math.max(top + minSizeY, bottom + deltaY);
+ }
+ mWindowDragBounds.set(left, top, right, bottom);
+ setActivityBounds(mWindowDragBounds);
+ mTaskResizingInProgress = false;
+ } else {
+ // This is a moving operation.
+ mWindowDragBounds.set(mWindowOriginalBounds);
+ mWindowDragBounds.offset(Math.round(e.getRawX() - mStartDragX),
+ Math.round(e.getRawY() - mStartDragY));
+ setActivityBounds(mWindowDragBounds);
+ }
}
break;
case MotionEvent.ACTION_UP:
- if (mDragging) {
- // Since the window is already where it should be we don't have to do anything
- // special at this time.
- mDragging = false;
+ if (!mDragging) {
+ break;
+ }
+ // Finsih the dragging now.
+ mDragging = false;
+ if (mSizeCorner == NONE) {
return true;
}
- break;
+
+ // Allow configuration changes again.
+ ActivityThread.currentActivityThread().suppressConfigurationChanges(false);
+ // Set the same bounds once more - which might trigger a configuration change now.
+ setActivityBounds(mWindowDragBounds);
+ // Tell the DecorView that we are done with out event interception by
+ // returning false.
+ return false;
case MotionEvent.ACTION_CANCEL:
- if (mDragging) {
- mDragging = false;
- setActivityBounds(mWindowOriginalBounds);
- return true;
+ if (!mDragging) {
+ break;
}
- break;
+ // Abort the ongoing dragging.
+ mDragging = false;
+ // Restore the previous bounds.
+ setActivityBounds(mWindowOriginalBounds);
+ if (mSizeCorner != NONE) {
+ // ALlow configuration changes again.
+ ActivityThread.currentActivityThread().suppressConfigurationChanges(false);
+ // Tell the DecorView that we are done with out event interception by
+ // returning false.
+ return false;
+ }
+ return true;
}
return mDragging;
}
@@ -244,6 +321,7 @@
boolean invisible = isFillingScreen() || !mShowDecor;
View caption = getChildAt(0);
caption.setVisibility(invisible ? GONE : VISIBLE);
+ mResizable = !invisible;
}
/**