Move window moving and resizing handling to WindowManager
- add a startMoving API to initiate a window move from app, once
the move starts WindowManager will take over the event handling.
- detect touch events along window's outside border and start
a resize if necessary
Change-Id: Ic7e8baba34e0aa27a43173e044ffb46e93e219e0
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6367a3d..f7dcf02 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2736,40 +2736,6 @@
}
/**
- * Returns the bounds of the task that contains this activity.
- *
- * @return Rect The bounds that contains the activity.
- * @hide
- */
- @Override
- public Rect getActivityBounds() throws RemoteException {
- return ActivityManagerNative.getDefault().getActivityBounds(mToken);
- }
-
- /**
- * Sets the bounds (size and position) of the task or stack that contains this
- * activity.
- * NOTE: The requested bounds might not the fully honored by the system depending
- * on the window placement policy.
- *
- * @param newBounds The new target bounds of the activity in task or stack.
- * @hide
- */
- @Override
- public void setActivityBounds(Rect newBounds) throws RemoteException {
- ActivityManagerNative.getDefault().setActivityBounds(mToken, newBounds);
- }
-
- /**
- * Activates this activity, hence bringing it to the top and giving it focus.
- * Note: This will only work for activities which are located on the freeform desktop.
- * @hide
- */
- public void activateActivity() throws RemoteException {
- ActivityManagerNative.getDefault().activateActivity(mToken);
- }
-
- /**
* Called to process key events. You can override this to intercept all
* key events before they are dispatched to the window. Be sure to call
* this implementation for key events that should be handled normally.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1f1f356..d1c73bc 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -752,24 +752,6 @@
return true;
}
- case GET_ACTIVITY_BOUNDS_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- IBinder token = data.readStrongBinder();
- Rect r = getActivityBounds(token);
- reply.writeNoException();
- r.writeToParcel(reply, 0);
- return true;
- }
-
- case SET_ACTIVITY_BOUNDS_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- IBinder token = data.readStrongBinder();
- Rect r = Rect.CREATOR.createFromParcel(data);
- setActivityBounds(token, r);
- reply.writeNoException();
- return true;
- }
-
case POSITION_TASK_IN_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int taskId = data.readInt();
@@ -827,14 +809,6 @@
return true;
}
- case ACTIVATE_ACTIVITY_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- IBinder token = data.readStrongBinder();
- activateActivity(token);
- reply.writeNoException();
- return true;
- }
-
case SET_FOCUSED_TASK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int taskId = data.readInt();
@@ -3631,18 +3605,6 @@
return focusedStackId;
}
@Override
- public void activateActivity(IBinder token) throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(token);
- mRemote.transact(ACTIVATE_ACTIVITY_TRANSACTION, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
- @Override
public void setFocusedTask(int taskId) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -5941,35 +5903,6 @@
}
@Override
- public void setActivityBounds(IBinder token, Rect r) throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(token);
- r.writeToParcel(data, 0);
- mRemote.transact(SET_ACTIVITY_BOUNDS_TRANSACTION, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
-
- @Override
- public Rect getActivityBounds(IBinder token) throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(token);
- mRemote.transact(GET_ACTIVITY_BOUNDS_TRANSACTION, data, reply, 0);
- reply.readException();
- Rect rect = Rect.CREATOR.createFromParcel(reply);
- data.recycle();
- reply.recycle();
- return rect;
- }
-
- @Override
public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 47abf26..66fa796 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -147,7 +147,6 @@
public boolean isInHomeStack(int taskId) throws RemoteException;
public void setFocusedStack(int stackId) throws RemoteException;
public int getFocusedStackId() throws RemoteException;
- public void activateActivity(IBinder token) throws RemoteException;
public void setFocusedTask(int taskId) throws RemoteException;
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException;
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
@@ -491,8 +490,6 @@
throws RemoteException;
public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException;
public void resizeTask(int taskId, Rect bounds) throws RemoteException;
- public void setActivityBounds(IBinder token, Rect bounds) throws RemoteException;
- public Rect getActivityBounds(IBinder token) throws RemoteException;
public Rect getTaskBounds(int taskId) throws RemoteException;
public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
@@ -891,7 +888,4 @@
int GET_ACTIVITY_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 343;
int MOVE_ACTIVITY_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 344;
int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
- int GET_ACTIVITY_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
- int SET_ACTIVITY_BOUNDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
- int ACTIVATE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 348;
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 73b4a6e..017364a 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -211,4 +211,12 @@
* The assumption is that this method will be called rather infrequently.
*/
void pokeDrawLock(IBinder window);
+
+ /**
+ * Starts a task window move with {startX, startY} as starting point. The amount of move
+ * will be the offset between {startX, startY} and the new cursor position.
+ *
+ * Returns true if the move started successfully; false otherwise.
+ */
+ boolean startMovingTask(IWindow window, float startX, float startY);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f4fc6e7..eb591c1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19750,6 +19750,26 @@
}
/**
+ * Starts a move from {startX, startY}, the amount of the movement will be the offset
+ * between {startX, startY} and the new cursor positon.
+ * @param startX horizontal coordinate where the move started.
+ * @param startY vertical coordinate where the move started.
+ * @return whether moving was started successfully.
+ * @hide
+ */
+ public final boolean startMovingTask(float startX, float startY) {
+ if (ViewDebug.DEBUG_POSITIONING) {
+ Log.d(VIEW_LOG_TAG, "startMovingTask: {" + startX + "," + startY + "}");
+ }
+ try {
+ return mAttachInfo.mSession.startMovingTask(mAttachInfo.mWindow, startX, startY);
+ } catch (RemoteException e) {
+ Log.e(VIEW_LOG_TAG, "Unable to start moving", e);
+ }
+ return false;
+ }
+
+ /**
* Handles drag events sent by the system following a call to
* {@link android.view.View#startDrag(ClipData,DragShadowBuilder,Object,int) startDrag()}.
*<p>
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8bf53a8..8278335 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -78,6 +78,12 @@
public static final boolean DEBUG_DRAG = false;
/**
+ * Enables detailed logging of task positioning operations.
+ * @hide
+ */
+ public static final boolean DEBUG_POSITIONING = false;
+
+ /**
* This annotation can be used to mark fields and methods to be dumped by
* the view server. Only non-void methods with no arguments can be annotated
* by this annotation.
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 5893f4a..b146a51 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -562,28 +562,6 @@
/** Returns the current stack Id for the window. */
int getWindowStackId() throws RemoteException;
-
- /**
- * Returns the bounds of the task that contains this activity.
- *
- * @return Rect The bounds that contains the activity.
- */
- Rect getActivityBounds() throws RemoteException;
-
- /**
- * Sets the bounds (size and position) of the task or stack that contains this
- * activity.
- * NOTE: The requested bounds might not the fully honored by the system depending
- * on the window placement policy.
- *
- * @param newBounds The new target bounds of the activity in task or stack.
- */
- void setActivityBounds(Rect newBounds) throws RemoteException;
-
- /**
- * Activates this activity, hence bringing it to the top and giving it focus.
- */
- void activateActivity() throws RemoteException;
}
public Window(Context context) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 791387d..7f01841 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -175,8 +175,6 @@
// 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;
@@ -2510,21 +2508,20 @@
@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 (mHasNonClientDecor && mNonClientDecorView.mVisible) {
+ // Don't dispatch ACTION_DOWN to the non client decor if the window is
+ // resizable and the event was (starting) outside the window.
+ // Window resizing events should be handled by WindowManager.
+ // TODO: Investigate how to handle the outside touch in window manager
+ // without generating these events.
+ // Currently we receive these because we need to enlarge the window's
+ // touch region so that the monitor channel receives the events
+ // in the outside touch area.
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;
+ return true;
}
}
}
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 7aa1920..6ab306c 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -16,7 +16,6 @@
package com.android.internal.widget;
-import android.app.ActivityThread;
import android.content.Context;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -71,33 +70,11 @@
// True if the window is being dragged.
private boolean mDragging = false;
- // The bounds of the window and the absolute mouse pointer coordinates from before we started to
- // drag the window. They will be used to determine the next window position.
- private final Rect mWindowOriginalBounds = new Rect();
- private float mStartDragX;
- private float mStartDragY;
// 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;
+ public boolean mVisible = false;
// The current focus state of the window for updating the window elevation.
private boolean mWindowHasFocus = true;
@@ -145,27 +122,14 @@
// When there is no decor we should not react to anything.
return false;
}
- // Ensure that the activity is active.
- activateActivity();
// 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 (!mDragging &&
(e.getToolType(e.getActionIndex()) != MotionEvent.TOOL_TYPE_MOUSE ||
(e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0)) {
mDragging = true;
- mWindowOriginalBounds.set(getActivityBounds());
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);
- }
+ startMovingTask(e.getRawX(), e.getRawY());
}
break;
@@ -178,79 +142,16 @@
mLeftMouseButtonReleased = true;
break;
}
- 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) {
- break;
- }
- // Finsih the dragging now.
- mDragging = false;
- if (mSizeCorner == NONE) {
- return true;
- }
-
- // 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) {
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;
@@ -323,7 +224,7 @@
boolean invisible = isFillingScreen() || !mShowDecor;
View caption = getChildAt(0);
caption.setVisibility(invisible ? GONE : VISIBLE);
- mResizable = !invisible;
+ mVisible = !invisible;
}
/**
@@ -390,54 +291,4 @@
}
}
}
-
- /**
- * Returns the bounds of this activity.
- * @return Returns bounds of the activity. It will return null if either the window is
- * fullscreen or the bounds could not be retrieved.
- */
- private Rect getActivityBounds() {
- Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
- if (callback != null) {
- try {
- return callback.getActivityBounds();
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to get the activity bounds.");
- }
- }
- return null;
- }
-
- /**
- * Sets the bounds of this Activity on the stack.
- * @param newBounds The bounds of the activity. Passing null is not allowed.
- */
- private void setActivityBounds(Rect newBounds) {
- if (newBounds == null) {
- Log.e(TAG, "Failed to set null bounds to the activity.");
- return;
- }
- Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
- if (callback != null) {
- try {
- callback.setActivityBounds(newBounds);
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to set the activity bounds.");
- }
- }
- }
-
- /**
- * Activates the activity - means setting the focus and moving it to the top of the stack.
- */
- private void activateActivity() {
- Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
- if (callback != null) {
- try {
- callback.activateActivity();
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to activate the activity.");
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 81936ee..14376d1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2730,31 +2730,17 @@
}
}
- /**
- * Activate an activity by bringing it to the top and set the focus on it.
- * Note: This is only allowed for activities which are on the freeform stack.
- * @param token The token of the activity calling which will get activated.
- */
@Override
- public void activateActivity(IBinder token) {
- if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "ActivateActivity token=" + token);
+ public void setFocusedTask(int taskId) {
+ if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
long callingId = Binder.clearCallingIdentity();
try {
synchronized (ActivityManagerService.this) {
- final ActivityRecord anyTaskRecord = ActivityRecord.isInStackLocked(token);
- if (anyTaskRecord == null) {
- Slog.w(TAG, "ActivateActivity: token=" + token + " not found");
- return;
- }
- TaskRecord task = anyTaskRecord.task;
- final boolean runsOnFreeformStack =
- task.stack.getStackId() == FREEFORM_WORKSPACE_STACK_ID;
- if (!runsOnFreeformStack) {
- Slog.w(TAG, "Tried to use activateActivity on a non freeform workspace!");
- } else if (task != null) {
- ActivityRecord topTaskRecord = task.topRunningActivityLocked(null);
- if (topTaskRecord != null) {
- setFocusedActivityLocked(topTaskRecord, "activateActivity");
+ TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task != null) {
+ ActivityRecord r = task.topRunningActivityLocked(null);
+ if (r != null) {
+ setFocusedActivityLocked(r, "setFocusedTask");
mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
}
}
@@ -2764,21 +2750,6 @@
}
}
- @Override
- public void setFocusedTask(int taskId) {
- if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
- synchronized (ActivityManagerService.this) {
- TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
- if (task != null) {
- ActivityRecord r = task.topRunningActivityLocked(null);
- if (r != null) {
- setFocusedActivityLocked(r, "setFocusedTask");
- mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
- }
- }
- }
- }
-
/** Sets the task stack listener that gets callbacks when a task stack changes. */
@Override
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
@@ -8721,58 +8692,6 @@
}
@Override
- public void setActivityBounds(IBinder token, Rect bounds) {
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- Slog.w(TAG, "setActivityBounds: token=" + token + " not found");
- return;
- }
- final TaskRecord task = r.task;
- if (task == null) {
- Slog.e(TAG, "setActivityBounds: No TaskRecord for the ActivityRecord r=" + r);
- return;
- }
- if (task.stack != null && task.stack.mStackId == DOCKED_STACK_ID) {
- mStackSupervisor.resizeStackLocked(task.stack.mStackId, bounds);
- } else {
- mStackSupervisor.resizeTaskLocked(task, bounds);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public Rect getActivityBounds(IBinder token) {
- long ident = Binder.clearCallingIdentity();
- Rect rect = null;
- try {
- synchronized (this) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- Slog.w(TAG, "getActivityBounds: token=" + token + " not found");
- return rect;
- }
- final TaskRecord task = r.task;
- if (task == null) {
- Slog.e(TAG, "getActivityBounds: No TaskRecord for the ActivityRecord r=" + r);
- return rect;
- }
- if (task.mBounds != null) {
- rect = new Rect(task.mBounds);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- return rect;
- }
-
- @Override
public Bitmap getTaskDescriptionIcon(String filename) {
if (!FileUtils.isValidExtFilename(filename)
|| !filename.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 99e9fe6..325196d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -21,8 +21,10 @@
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerService.TAG;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
+import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
@@ -240,17 +242,85 @@
return -1;
}
+ /**
+ * Find the id of the task whose outside touch area (for resizing) (x, y)
+ * falls within. Returns -1 if the touch doesn't fall into a resizing area.
+ */
+ int taskIdForControlPoint(int x, int y) {
+ final int delta = calculatePixelFromDp(WindowState.RESIZE_HANDLE_WIDTH_IN_DP);
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ TaskStack stack = mStacks.get(stackNdx);
+ if (!stack.allowTaskResize()) {
+ break;
+ }
+ final ArrayList<Task> tasks = stack.getTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final Task task = tasks.get(taskNdx);
+ if (task.isFullscreen()) {
+ return -1;
+ }
+ task.getBounds(mTmpRect);
+ mTmpRect.inset(-delta, -delta);
+ if (mTmpRect.contains(x, y)) {
+ mTmpRect.inset(delta, delta);
+ if (!mTmpRect.contains(x, y)) {
+ return task.mTaskId;
+ }
+ // User touched inside the task. No need to look further,
+ // focus transfer will be handled in ACTION_UP.
+ return -1;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private int calculatePixelFromDp(int dp) {
+ final Configuration serviceConfig = mService.mCurConfiguration;
+ // TODO(multidisplay): Update Dp to that of display stack is on.
+ final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ return (int)(dp * density);
+ }
+
void setTouchExcludeRegion(Task focusedTask) {
mTouchExcludeRegion.set(mBaseDisplayRect);
WindowList windows = getWindowList();
+ final int delta = calculatePixelFromDp(WindowState.RESIZE_HANDLE_WIDTH_IN_DP);
for (int i = windows.size() - 1; i >= 0; --i) {
final WindowState win = windows.get(i);
- final Task task = win.getTask();
- if (win.isVisibleLw() && task != null && task != focusedTask) {
- mTmpRect.set(win.mVisibleFrame);
- // If no intersection, we need mTmpRect to be unmodified.
- mTmpRect.intersect(win.mVisibleInsets);
- mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+ final Task task = win.mAppToken != null ? win.getTask() : null;
+ if (win.isVisibleLw() && task != null) {
+ /**
+ * Exclusion region is the region that TapDetector doesn't care about.
+ * Here we want to remove all non-focused tasks from the exclusion region.
+ * We also remove the outside touch area for resizing for all freeform
+ * tasks (including the focused).
+ *
+ * (For freeform focused task, the below logic will first remove the enlarged
+ * area, then add back the inner area.)
+ */
+ final boolean isFreeformed = win.inFreeformWorkspace();
+ if (task != focusedTask || isFreeformed) {
+ mTmpRect.set(win.mVisibleFrame);
+ mTmpRect.intersect(win.mVisibleInsets);
+ /**
+ * If the task is freeformed, enlarge the area to account for outside
+ * touch area for resize.
+ */
+ if (isFreeformed) {
+ mTmpRect.inset(-delta, -delta);
+ }
+ mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+ }
+ /**
+ * If we removed the focused task above, add it back and only leave its
+ * outside touch area in the exclusion. TapDectector is not interested in
+ * any touch inside the focused task itself.
+ */
+ if (task == focusedTask && isFreeformed) {
+ mTmpRect.inset(delta, delta);
+ mTouchExcludeRegion.op(mTmpRect, Region.Op.UNION);
+ }
}
}
if (mTapDetector != null) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index e87dcde..8f77176 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -283,11 +283,12 @@
// stop intercepting input
mService.mDragState.unregister();
- mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
// free our resources and drop all the object references
mService.mDragState.reset();
mService.mDragState = null;
+
+ mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
}
void notifyMoveLw(float x, float y) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b3244ff..9adcafc 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -64,9 +64,9 @@
public InputMonitor(WindowManagerService service) {
mService = service;
}
-
+
/* Notifies the window manager about a broken input channel.
- *
+ *
* Called by the InputManager.
*/
@Override
@@ -83,10 +83,10 @@
}
}
}
-
+
/* Notifies the window manager about an application that is not responding.
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
- *
+ *
* Called by the InputManager.
*/
@Override
@@ -257,6 +257,20 @@
}
}
+ final boolean inPositioning = (mService.mTaskPositioner != null);
+ if (inPositioning) {
+ if (WindowManagerService.DEBUG_TASK_POSITIONING) {
+ Log.d(WindowManagerService.TAG, "Inserting window handle for repositioning");
+ }
+ final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle;
+ if (dragWindowHandle != null) {
+ addInputWindowHandleLw(dragWindowHandle);
+ } else {
+ Slog.e(WindowManagerService.TAG,
+ "Repositioning is in progress but there is no drag window handle.");
+ }
+ }
+
boolean addInputConsumerHandle = mService.mInputConsumer != null;
// Add all windows on the default display.
@@ -437,56 +451,56 @@
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
}
-
+
window.paused = true;
updateInputWindowsLw(true /*force*/);
}
}
-
+
public void resumeDispatchingLw(WindowToken window) {
if (window.paused) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
}
-
+
window.paused = false;
updateInputWindowsLw(true /*force*/);
}
}
-
+
public void freezeInputDispatchingLw() {
if (! mInputDispatchFrozen) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Freezing input dispatching");
}
-
+
mInputDispatchFrozen = true;
updateInputDispatchModeLw();
}
}
-
+
public void thawInputDispatchingLw() {
if (mInputDispatchFrozen) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Thawing input dispatching");
}
-
+
mInputDispatchFrozen = false;
updateInputDispatchModeLw();
}
}
-
+
public void setEventDispatchingLw(boolean enabled) {
if (mInputDispatchEnabled != enabled) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Setting event dispatching to " + enabled);
}
-
+
mInputDispatchEnabled = enabled;
updateInputDispatchModeLw();
}
}
-
+
private void updateInputDispatchModeLw() {
mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 12f61f9..1f62bc1 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -347,6 +347,13 @@
return true; // success!
}
+ public boolean startMovingTask(IWindow window, float startX, float startY) {
+ if (WindowManagerService.DEBUG_TASK_POSITIONING) Slog.d(
+ WindowManagerService.TAG, "startMovingTask: {" + startX + "," + startY + "}");
+
+ return mService.startMovingTask(window, startX, startY);
+ }
+
public void reportDropResult(IWindow window, boolean consumed) {
IBinder token = window.asBinder();
if (WindowManagerService.DEBUG_DRAG) {
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
new file mode 100644
index 0000000..71b83a5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2015 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.server.wm;
+
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.input.InputWindowHandle;
+import com.android.server.wm.WindowManagerService.H;
+import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import android.annotation.IntDef;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Looper;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+class TaskPositioner {
+ private static final String TAG = "TaskPositioner";
+
+ @IntDef(flag = true,
+ value = {
+ CTRL_NONE,
+ CTRL_LEFT,
+ CTRL_RIGHT,
+ CTRL_TOP,
+ CTRL_BOTTOM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CtrlType {}
+
+ private static final int CTRL_NONE = 0x0;
+ private static final int CTRL_LEFT = 0x1;
+ private static final int CTRL_RIGHT = 0x2;
+ private static final int CTRL_TOP = 0x4;
+ private static final int CTRL_BOTTOM = 0x8;
+
+ private final WindowManagerService mService;
+ private WindowPositionerEventReceiver mInputEventReceiver;
+ private Display mDisplay;
+ private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+
+ private int mTaskId;
+ private final Rect mWindowOriginalBounds = new Rect();
+ private final Rect mWindowDragBounds = new Rect();
+ private float mStartDragX;
+ private float mStartDragY;
+ @CtrlType
+ private int mCtrlType = CTRL_NONE;
+
+ InputChannel mServerChannel;
+ InputChannel mClientChannel;
+ InputApplicationHandle mDragApplicationHandle;
+ InputWindowHandle mDragWindowHandle;
+
+ private final class WindowPositionerEventReceiver extends InputEventReceiver {
+ public WindowPositionerEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (!(event instanceof MotionEvent)
+ || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
+ return;
+ }
+ final MotionEvent motionEvent = (MotionEvent) event;
+ boolean handled = false;
+
+ try {
+ boolean endDrag = false;
+ final float newX = motionEvent.getRawX();
+ final float newY = motionEvent.getRawY();
+
+ switch (motionEvent.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
+ }
+ } break;
+
+ case MotionEvent.ACTION_MOVE: {
+ if (DEBUG_TASK_POSITIONING){
+ Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
+ }
+ synchronized (mService.mWindowMap) {
+ notifyMoveLocked(newX, newY);
+ }
+ try {
+ mService.mActivityManager.resizeTask(mTaskId, mWindowDragBounds);
+ } catch(RemoteException e) {}
+ } break;
+
+ case MotionEvent.ACTION_UP: {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
+ }
+ endDrag = true;
+ } break;
+
+ case MotionEvent.ACTION_CANCEL: {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
+ }
+ endDrag = true;
+ } break;
+ }
+
+ if (endDrag) {
+ // Post back to WM to handle clean-ups. We still need the input
+ // event handler for the last finishInputEvent()!
+ mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
+ }
+ handled = true;
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception caught by drag handleMotion", e);
+ } finally {
+ finishInputEvent(event, handled);
+ }
+ }
+ }
+
+ TaskPositioner(WindowManagerService service) {
+ mService = service;
+ }
+
+ /**
+ * @param display The Display that the window being dragged is on.
+ */
+ void register(Display display) {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "Registering task positioner");
+ }
+
+ if (mClientChannel != null) {
+ Slog.e(TAG, "Task positioner already registered");
+ return;
+ }
+
+ mDisplay = display;
+ mDisplay.getMetrics(mDisplayMetrics);
+ final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
+ mServerChannel = channels[0];
+ mClientChannel = channels[1];
+ mService.mInputManager.registerInputChannel(mServerChannel, null);
+
+ mInputEventReceiver = new WindowPositionerEventReceiver(mClientChannel,
+ mService.mH.getLooper());
+
+ mDragApplicationHandle = new InputApplicationHandle(null);
+ mDragApplicationHandle.name = TAG;
+ mDragApplicationHandle.dispatchingTimeoutNanos =
+ WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
+ mDisplay.getDisplayId());
+ mDragWindowHandle.name = TAG;
+ mDragWindowHandle.inputChannel = mServerChannel;
+ mDragWindowHandle.layer = getDragLayerLocked();
+ mDragWindowHandle.layoutParamsFlags = 0;
+ mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
+ mDragWindowHandle.dispatchingTimeoutNanos =
+ WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ mDragWindowHandle.visible = true;
+ mDragWindowHandle.canReceiveKeys = false;
+ mDragWindowHandle.hasFocus = true;
+ mDragWindowHandle.hasWallpaper = false;
+ mDragWindowHandle.paused = false;
+ mDragWindowHandle.ownerPid = Process.myPid();
+ mDragWindowHandle.ownerUid = Process.myUid();
+ mDragWindowHandle.inputFeatures = 0;
+ mDragWindowHandle.scaleFactor = 1.0f;
+
+ // The drag window cannot receive new touches.
+ mDragWindowHandle.touchableRegion.setEmpty();
+
+ // The drag window covers the entire display
+ mDragWindowHandle.frameLeft = 0;
+ mDragWindowHandle.frameTop = 0;
+ final Point p = new Point();
+ mDisplay.getRealSize(p);
+ mDragWindowHandle.frameRight = p.x;
+ mDragWindowHandle.frameBottom = p.y;
+
+ // Pause rotations before a drag.
+ if (WindowManagerService.DEBUG_ORIENTATION) {
+ Slog.d(TAG, "Pausing rotation during re-position");
+ }
+ mService.pauseRotationLocked();
+ }
+
+ void unregister() {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "Unregistering task positioner");
+ }
+
+ if (mClientChannel == null) {
+ Slog.e(TAG, "Task positioner not registered");
+ return;
+ }
+
+ mService.mInputManager.unregisterInputChannel(mServerChannel);
+
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
+ mClientChannel.dispose();
+ mServerChannel.dispose();
+ mClientChannel = null;
+ mServerChannel = null;
+
+ mDragWindowHandle = null;
+ mDragApplicationHandle = null;
+ mDisplay = null;
+
+ // Resume rotations after a drag.
+ if (WindowManagerService.DEBUG_ORIENTATION) {
+ Slog.d(TAG, "Resuming rotation after re-position");
+ }
+ mService.resumeRotationLocked();
+ }
+
+ void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
+ if (DEBUG_TASK_POSITIONING) {Slog.d(TAG,
+ "startDragLocked: win=" + win + ", resize=" + resize
+ + ", {" + startX + ", " + startY + "}");
+ }
+ mCtrlType = CTRL_NONE;
+ if (resize) {
+ final Rect visibleFrame = win.mVisibleFrame;
+ if (startX < visibleFrame.left) {
+ mCtrlType |= CTRL_LEFT;
+ }
+ if (startX > visibleFrame.right) {
+ mCtrlType |= CTRL_RIGHT;
+ }
+ if (startY < visibleFrame.top) {
+ mCtrlType |= CTRL_TOP;
+ }
+ if (startY > visibleFrame.bottom) {
+ mCtrlType |= CTRL_BOTTOM;
+ }
+ }
+
+ final Task task = win.getTask();
+ mTaskId = task.mTaskId;
+ mStartDragX = startX;
+ mStartDragY = startY;
+
+ mService.getTaskBounds(mTaskId, mWindowOriginalBounds);
+ }
+
+ private void notifyMoveLocked(float x, float y) {
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
+ }
+
+ if (mCtrlType != CTRL_NONE) {
+ // This is a resizing operation.
+ final int deltaX = Math.round(x - mStartDragX);
+ final int deltaY = Math.round(y - mStartDragY);
+ // TODO: fix the min sizes when we have mininum width/height support,
+ // use hard-coded min sizes for now.
+ 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 ((mCtrlType & CTRL_LEFT) != 0) {
+ left = Math.min(left + deltaX, right - minSizeX);
+ }
+ if ((mCtrlType & CTRL_TOP) != 0) {
+ top = Math.min(top + deltaY, bottom - minSizeY);
+ }
+ if ((mCtrlType & CTRL_RIGHT) != 0) {
+ right = Math.max(left + minSizeX, right + deltaX);
+ }
+ if ((mCtrlType & CTRL_BOTTOM) != 0) {
+ bottom = Math.max(top + minSizeY, bottom + deltaY);
+ }
+ mWindowDragBounds.set(left, top, right, bottom);
+ } else {
+ // This is a moving operation.
+ mWindowDragBounds.set(mWindowOriginalBounds);
+ mWindowDragBounds.offset(Math.round(x - mStartDragX),
+ Math.round(y - mStartDragY));
+ }
+ }
+
+ private int getDragLayerLocked() {
+ return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
+ * WindowManagerService.TYPE_LAYER_MULTIPLIER
+ + WindowManagerService.TYPE_LAYER_OFFSET;
+ }
+
+ private float dipToPx(float dip) {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, mDisplayMetrics);
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index c3a8486..ae3bb9b 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -93,6 +93,11 @@
}
}
+ boolean allowTaskResize() {
+ return mStackId == FREEFORM_WORKSPACE_STACK_ID
+ || mStackId == DOCKED_STACK_ID;
+ }
+
/**
* Set the bounds of the stack and its containing tasks.
* @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index c97d12f..ce1b785 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -47,12 +47,23 @@
public void onPointerEvent(MotionEvent motionEvent) {
final int action = motionEvent.getAction();
switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_DOWN: {
mPointerId = motionEvent.getPointerId(0);
mDownX = motionEvent.getX();
mDownY = motionEvent.getY();
+
+ final int x = (int) mDownX;
+ final int y = (int) mDownY;
+ synchronized (this) {
+ if (!mTouchExcludeRegion.contains(x, y)) {
+ mService.mH.obtainMessage(H.TAP_DOWN_OUTSIDE_TASK, x, y,
+ mDisplayContent).sendToTarget();
+ }
+ }
break;
- case MotionEvent.ACTION_MOVE:
+ }
+
+ case MotionEvent.ACTION_MOVE: {
if (mPointerId >= 0) {
int index = motionEvent.findPointerIndex(mPointerId);
if ((motionEvent.getEventTime() - motionEvent.getDownTime()) > TAP_TIMEOUT_MSEC
@@ -63,6 +74,8 @@
}
}
break;
+ }
+
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP: {
int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 84e0b65..d345aca 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -207,6 +207,7 @@
static final boolean DEBUG_SURFACE_TRACE = false;
static final boolean DEBUG_WINDOW_TRACE = false;
static final boolean DEBUG_TASK_MOVEMENT = false;
+ static final boolean DEBUG_TASK_POSITIONING = false;
static final boolean DEBUG_STACK = false;
static final boolean DEBUG_DISPLAY = false;
static final boolean DEBUG_POWER = false;
@@ -598,6 +599,7 @@
// Whether or not a layout can cause a wake up when theater mode is enabled.
boolean mAllowTheaterModeWakeFromLayout;
+ TaskPositioner mTaskPositioner;
DragState mDragState = null;
// For frozen screen animations.
@@ -6817,6 +6819,88 @@
}
}
+ boolean startMovingTask(IWindow window, float startX, float startY) {
+ WindowState callingWin = null;
+ synchronized (mWindowMap) {
+ callingWin = windowForClientLocked(null, window, false);
+ if (!startPositioningLocked(callingWin, false /*resize*/, startX, startY)) {
+ return false;
+ }
+ }
+ try {
+ mActivityManager.setFocusedTask(callingWin.getTask().mTaskId);
+ } catch(RemoteException e) {}
+ return true;
+ }
+
+ private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
+ int taskId = -1;
+ AppWindowToken atoken = null;
+ synchronized (mWindowMap) {
+ taskId = displayContent.taskIdForControlPoint(startX, startY);
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null || task.mAppTokens == null) {
+ return;
+ }
+ AppTokenList tokens = task.mAppTokens;
+ atoken = tokens.get(tokens.size() - 1);
+ WindowState win = atoken.findMainWindow();
+ if (!startPositioningLocked(win, true /*resize*/, startX, startY)) {
+ return;
+ }
+ }
+ try {
+ mActivityManager.setFocusedTask(taskId);
+ } catch(RemoteException e) {}
+ }
+
+ private boolean startPositioningLocked(
+ WindowState win, boolean resize, float startX, float startY) {
+ if (WindowManagerService.DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "startPositioningLocked: win=" + win +
+ ", resize=" + resize + ", {" + startX + ", " + startY + "}");
+ }
+ if (win == null || win.getAppToken() == null || !win.inFreeformWorkspace()) {
+ Slog.w(TAG, "startPositioningLocked: Bad window " + win);
+ return false;
+ }
+
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent == null) {
+ Slog.w(TAG, "startPositioningLocked: Invalid display content " + win);
+ return false;
+ }
+
+ Display display = displayContent.getDisplay();
+ mTaskPositioner = new TaskPositioner(this);
+ mTaskPositioner.register(display);
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+ if (!mInputManager.transferTouchFocus(
+ win.mInputChannel, mTaskPositioner.mServerChannel)) {
+ Slog.e(TAG, "startPositioningLocked: Unable to transfer touch focus");
+ mTaskPositioner.unregister();
+ mTaskPositioner = null;
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+ return false;
+ }
+
+ mTaskPositioner.startDragLocked(win, resize, startX, startY);
+ return true;
+ }
+
+ private void finishPositioning() {
+ if (WindowManagerService.DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "finishPositioning");
+ }
+ synchronized (mWindowMap) {
+ if (mTaskPositioner != null) {
+ mTaskPositioner.unregister();
+ mTaskPositioner = null;
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+ }
+ }
+ }
+
// -------------------------------------------------------------
// Drag and drop
// -------------------------------------------------------------
@@ -7082,6 +7166,9 @@
public static final int RESET_ANR_MESSAGE = 38;
public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39;
+ public static final int TAP_DOWN_OUTSIDE_TASK = 40;
+ public static final int FINISH_TASK_POSITIONING = 41;
+
@Override
public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) {
@@ -7542,6 +7629,17 @@
}
}
break;
+
+ case TAP_DOWN_OUTSIDE_TASK: {
+ startResizingTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
+ }
+ break;
+
+ case FINISH_TASK_POSITIONING: {
+ finishPositioning();
+ }
+ break;
+
case NOTIFY_ACTIVITY_DRAWN:
try {
mActivityManager.notifyActivityDrawn((IBinder) msg.obj);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 779f342..789354d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -86,7 +86,7 @@
// The thickness of a window resize handle outside the window bounds on the free form workspace
// to capture touch events in that area.
- private static final int RESIZE_HANDLE_WIDTH_IN_DP = 10;
+ static final int RESIZE_HANDLE_WIDTH_IN_DP = 10;
static final boolean BOUNDS_FOR_TOUCH = true;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index bea1f86..9824fa1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -148,6 +148,13 @@
}
@Override
+ public boolean startMoving(IWindow window, float startX, float startY)
+ throws RemoteException {
+ // pass for now
+ return false;
+ }
+
+ @Override
public void reportDropResult(IWindow window, boolean consumed) throws RemoteException {
// pass for now
}