Fixes for touch exclusion region
- Evaluate touch exclusion region on per-task basis, instead of per
window. Smallest unit for exclusion is Task, if task has more than
one app window, we still need to use the visible area of the task
to do focus transfer properly.
- Only add back the touch region for focused task after all tasks
are processed for eclusion, otherwise the focused area could be
removed from exclusion incorrectly.
- Skip app windows that's exiting or hidden.
bug: 25494928
Change-Id: I972ba2149431557e7420b1095d9496a6cd821bdb
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f54fd83..e264c43 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -291,16 +291,18 @@
final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = tasks.get(taskNdx);
- // We need to use the visible frame on the window for any touch-related tests.
- // Can't use the task's bounds because the original task bounds might be adjusted
- // to fit the content frame. For example, the presence of the IME adjusting the
+ final WindowState win = task.getTopVisibleAppMainWindow();
+ if (win == null) {
+ continue;
+ }
+ // We need to use the task's dim bounds (which is derived from the visible
+ // bounds of its apps windows) for any touch-related tests. Can't use
+ // the task's original bounds because it might be adjusted to fit the
+ // content frame. For example, the presence of the IME adjusting the
// windows frames when the app window is the IME target.
- final WindowState win = task.getTopAppMainWindow();
- if (win != null) {
- win.getVisibleBounds(mTmpRect);
- if (mTmpRect.contains(x, y)) {
- return task.mTaskId;
- }
+ task.getDimBounds(mTmpRect);
+ if (mTmpRect.contains(x, y)) {
+ return task.mTaskId;
}
}
}
@@ -308,10 +310,10 @@
}
/**
- * Find the window whose outside touch area (for resizing) (x, y) falls within.
+ * Find the task whose outside touch area (for resizing) (x, y) falls within.
* Returns null if the touch doesn't fall into a resizing area.
*/
- WindowState findWindowForControlPoint(int x, int y) {
+ Task findTaskForControlPoint(int x, int y) {
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
TaskStack stack = mStacks.get(stackNdx);
@@ -325,24 +327,22 @@
return null;
}
- // We need to use the visible frame on the window for any touch-related
- // tests. Can't use the task's bounds because the original task bounds
- // might be adjusted to fit the content frame. (One example is when the
- // task is put to top-left quadrant, the actual visible frame would not
- // start at (0,0) after it's adjusted for the status bar.)
- final WindowState win = task.getTopAppMainWindow();
- if (win != null) {
- win.getVisibleBounds(mTmpRect);
- mTmpRect.inset(-delta, -delta);
- if (mTmpRect.contains(x, y)) {
- mTmpRect.inset(delta, delta);
- if (!mTmpRect.contains(x, y)) {
- return win;
- }
- // User touched inside the task. No need to look further,
- // focus transfer will be handled in ACTION_UP.
- return null;
+ // We need to use the task's dim bounds (which is derived from the visible
+ // bounds of its apps windows) for any touch-related tests. Can't use
+ // the task's original bounds because it might be adjusted to fit the
+ // content frame. One example is when the task is put to top-left quadrant,
+ // the actual visible area would not start at (0,0) after it's adjusted
+ // for the status bar.
+ task.getDimBounds(mTmpRect);
+ mTmpRect.inset(-delta, -delta);
+ if (mTmpRect.contains(x, y)) {
+ mTmpRect.inset(delta, delta);
+ if (!mTmpRect.contains(x, y)) {
+ return task;
}
+ // User touched inside the task. No need to look further,
+ // focus transfer will be handled in ACTION_UP.
+ return null;
}
}
}
@@ -351,12 +351,18 @@
void setTouchExcludeRegion(Task focusedTask) {
mTouchExcludeRegion.set(mBaseDisplayRect);
- WindowList windows = getWindowList();
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
- 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) {
+ boolean addBackFocusedTask = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ TaskStack stack = mStacks.get(stackNdx);
+ final ArrayList<Task> tasks = stack.getTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final Task task = tasks.get(taskNdx);
+ final WindowState win = task.getTopVisibleAppMainWindow();
+ if (win == null) {
+ continue;
+ }
+
/**
* Exclusion region is the region that TapDetector doesn't care about.
* Here we want to remove all non-focused tasks from the exclusion region.
@@ -368,13 +374,17 @@
*/
final boolean isFreeformed = task.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.
- */
+ task.getDimBounds(mTmpRect);
if (isFreeformed) {
+ // If we're removing a freeform, focused app from the exclusion region,
+ // we need to add back its touchable frame later. Remember the touchable
+ // frame now.
+ if (task == focusedTask) {
+ addBackFocusedTask = true;
+ mTmpRect2.set(mTmpRect);
+ }
+ // If the task is freeformed, enlarge the area to account for outside
+ // touch area for resize.
mTmpRect.inset(-delta, -delta);
// Intersect with display content rect. If we have system decor (status bar/
// navigation bar), we want to exclude that from the tap detection.
@@ -385,17 +395,14 @@
}
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 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 (addBackFocusedTask) {
+ mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
+ }
if (mTapDetector != null) {
mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1b86488..46cd7cd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -284,7 +284,12 @@
boolean getMaxVisibleBounds(Rect out) {
boolean foundTop = false;
for (int i = mAppTokens.size() - 1; i >= 0; i--) {
- final WindowState win = mAppTokens.get(i).findMainWindow();
+ final AppWindowToken token = mAppTokens.get(i);
+ // skip hidden (or about to hide) apps
+ if (token.mIsExiting || token.clientHidden || token.hiddenRequested) {
+ continue;
+ }
+ final WindowState win = token.findMainWindow();
if (win == null) {
continue;
}
@@ -413,14 +418,20 @@
return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
}
- WindowState getTopAppMainWindow() {
- final int tokensCount = mAppTokens.size();
- return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
+ WindowState getTopVisibleAppMainWindow() {
+ final AppWindowToken token = getTopVisibleAppToken();
+ return token != null ? token.findMainWindow() : null;
}
- AppWindowToken getTopAppWindowToken() {
- final int tokensCount = mAppTokens.size();
- return tokensCount > 0 ? mAppTokens.get(tokensCount - 1) : null;
+ AppWindowToken getTopVisibleAppToken() {
+ for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+ final AppWindowToken token = mAppTokens.get(i);
+ // skip hidden (or about to hide) apps
+ if (!token.mIsExiting && !token.clientHidden && !token.hiddenRequested) {
+ return token;
+ }
+ }
+ return null;
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index dd47e7a..f5e4e3b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -332,30 +332,34 @@
+ ", {" + startX + ", " + startY + "}");
}
mCtrlType = CTRL_NONE;
+ mTask = win.getTask();
+ mStartDragX = startX;
+ mStartDragY = startY;
+
+ // Use the dim bounds, not the original task bounds. The cursor
+ // movement should be calculated relative to the visible bounds.
+ // Also, use the dim bounds of the task which accounts for
+ // multiple app windows. Don't use any bounds from win itself as it
+ // may not be the same size as the task.
+ mTask.getDimBounds(mTmpRect);
+
if (resize) {
- final Rect visibleFrame = win.mVisibleFrame;
- if (startX < visibleFrame.left) {
+ if (startX < mTmpRect.left) {
mCtrlType |= CTRL_LEFT;
}
- if (startX > visibleFrame.right) {
+ if (startX > mTmpRect.right) {
mCtrlType |= CTRL_RIGHT;
}
- if (startY < visibleFrame.top) {
+ if (startY < mTmpRect.top) {
mCtrlType |= CTRL_TOP;
}
- if (startY > visibleFrame.bottom) {
+ if (startY > mTmpRect.bottom) {
mCtrlType |= CTRL_BOTTOM;
}
mResizing = true;
}
- mTask = win.getTask();
- mStartDragX = startX;
- mStartDragY = startY;
-
- // Use the visible bounds, not the original task bounds. The cursor
- // movement should be calculated relative to the visible bounds.
- mWindowOriginalBounds.set(win.mVisibleFrame);
+ mWindowOriginalBounds.set(mTmpRect);
}
private void endDragLocked() {
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 1fe359e..f5b83bb 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -90,11 +90,11 @@
case MotionEvent.ACTION_HOVER_MOVE: {
final int x = (int) motionEvent.getX();
final int y = (int) motionEvent.getY();
- final WindowState window = mDisplayContent.findWindowForControlPoint(x, y);
- if (window == null) {
+ final Task task = mDisplayContent.findTaskForControlPoint(x, y);
+ if (task == null) {
break;
}
- window.getVisibleBounds(mTmpRect);
+ task.getDimBounds(mTmpRect);
if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) {
int iconShape = STYLE_DEFAULT;
if (x < mTmpRect.left) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f17698c..a22f821 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7069,15 +7069,16 @@
}
private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
- WindowState win = null;
+ Task task = null;
synchronized (mWindowMap) {
- win = displayContent.findWindowForControlPoint(startX, startY);
- if (win == null || !startPositioningLocked(win, true /*resize*/, startX, startY)) {
+ task = displayContent.findTaskForControlPoint(startX, startY);
+ if (task == null || !startPositioningLocked(
+ task.getTopVisibleAppMainWindow(), true /*resize*/, startX, startY)) {
return;
}
}
try {
- mActivityManager.setFocusedTask(win.getTask().mTaskId);
+ mActivityManager.setFocusedTask(task.mTaskId);
} catch(RemoteException e) {}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 93b40c0..32ebfb2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1698,7 +1698,7 @@
Task task = getTask();
if (task == null || task.inHomeStack()
- || task.getTopAppWindowToken() != mAppToken) {
+ || task.getTopVisibleAppToken() != mAppToken) {
// Don't save surfaces for home stack apps. These usually resume and draw
// first frame very fast. Saving surfaces are mostly a waste of memory.
// Don't save if the window is not the topmost window.