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/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;