Set ActivityView's rect as tap exclude region
Track bounds of an ActivityView and set them as a tap exclude region,
so that taps on this area won't cause a focus switch between
hosting activity and activities inside of ActivityView.
Bug: 63902362
Test: Manual with ActivityView test app
Change-Id: I3cdafe32e0bdf414507fef0d622d9c140eee3188
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a8e00dd..531409f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -134,6 +134,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.MutableBoolean;
import android.util.Slog;
@@ -330,6 +331,8 @@
final PinnedStackController mPinnedStackControllerLocked;
final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
+ /** A collection of windows that provide tap exclude regions inside of them. */
+ final ArraySet<WindowState> mTapExcludeProvidingWindows = new ArraySet<>();
private boolean mHaveBootMsg = false;
private boolean mHaveApp = false;
@@ -1866,10 +1869,14 @@
}
}
for (int i = mTapExcludedWindows.size() - 1; i >= 0; i--) {
- WindowState win = mTapExcludedWindows.get(i);
+ final WindowState win = mTapExcludedWindows.get(i);
win.getTouchableRegion(mTmpRegion);
mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
}
+ for (int i = mTapExcludeProvidingWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = mTapExcludeProvidingWindows.valueAt(i);
+ win.amendTapExcludeRegion(mTouchExcludeRegion);
+ }
// TODO(multi-display): Support docked stacks on secondary displays.
if (mDisplayId == DEFAULT_DISPLAY && getSplitScreenPrimaryStack() != null) {
mDividerControllerLocked.getTouchRegion(mTmpRect);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 192d6c8..334be33 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -467,6 +467,17 @@
}
}
+ @Override
+ public void updateTapExcludeRegion(IWindow window, int regionId, int left, int top, int width,
+ int height) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mService.updateTapExcludeRegion(window, regionId, left, top, width, height);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
void windowAddedLocked(String packageName) {
mPackageName = packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
diff --git a/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java
new file mode 100644
index 0000000..cbc936f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TapExcludeRegionHolder.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 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 android.graphics.Rect;
+import android.graphics.Region;
+import android.util.SparseArray;
+
+/**
+ * A holder that contains a collection of rectangular areas identified by int id. Each individual
+ * region can be updated separately.
+ */
+class TapExcludeRegionHolder {
+ private SparseArray<Rect> mTapExcludeRects = new SparseArray<>();
+
+ /** Update the specified region with provided position and size. */
+ void updateRegion(int regionId, int left, int top, int width, int height) {
+ if (width <= 0 || height <= 0) {
+ // A region became empty - remove it.
+ mTapExcludeRects.remove(regionId);
+ return;
+ }
+
+ Rect region = mTapExcludeRects.get(regionId);
+ if (region == null) {
+ region = new Rect();
+ }
+ region.set(left, top, left + width, top + height);
+ mTapExcludeRects.put(regionId, region);
+ }
+
+ /**
+ * Union the provided region with current region formed by this container.
+ */
+ void amendRegion(Region region, Rect boundingRegion) {
+ for (int i = mTapExcludeRects.size() - 1; i>= 0 ; --i) {
+ final Rect rect = mTapExcludeRects.valueAt(i);
+ rect.intersect(boundingRegion);
+ region.union(rect);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a91eb4f..20d8028 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6923,6 +6923,23 @@
}
}
+ /**
+ * Update a tap exclude region with a rectangular area in the window identified by the provided
+ * id. Touches on this region will not switch focus to this window. Passing an empty rect will
+ * remove the area from the exclude region of this window.
+ */
+ void updateTapExcludeRegion(IWindow client, int regionId, int left, int top, int width,
+ int height) {
+ synchronized (mWindowMap) {
+ final WindowState callingWin = windowForClientLocked(null, client, false);
+ if (callingWin == null) {
+ Slog.w(TAG_WM, "Bad requesting window " + client);
+ return;
+ }
+ callingWin.updateTapExcludeRegion(regionId, left, top, width, height);
+ }
+ }
+
@Override
public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
throws RemoteException {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index db30db0..477dd2b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -630,6 +630,11 @@
private final Point mSurfacePosition = new Point();
/**
+ * A region inside of this window to be excluded from touch-related focus switches.
+ */
+ private TapExcludeRegionHolder mTapExcludeRegionHolder;
+
+ /**
* Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
* of z-order and 1 otherwise.
*/
@@ -1870,6 +1875,11 @@
if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
dc.mTapExcludedWindows.remove(this);
}
+ if (mTapExcludeRegionHolder != null) {
+ // If a tap exclude region container was initialized for this window, then it should've
+ // also been registered in display.
+ dc.mTapExcludeProvidingWindows.remove(this);
+ }
mPolicy.removeWindowLw(this);
disposeInputChannel();
@@ -4620,6 +4630,37 @@
}
}
+ /**
+ * Update a tap exclude region with a rectangular area identified by provided id. The requested
+ * area will be clipped to the window bounds.
+ */
+ void updateTapExcludeRegion(int regionId, int left, int top, int width, int height) {
+ final DisplayContent currentDisplay = getDisplayContent();
+ if (currentDisplay == null) {
+ throw new IllegalStateException("Trying to update window not attached to any display.");
+ }
+
+ if (mTapExcludeRegionHolder == null) {
+ mTapExcludeRegionHolder = new TapExcludeRegionHolder();
+
+ // Make sure that this window is registered as one that provides a tap exclude region
+ // for its containing display.
+ currentDisplay.mTapExcludeProvidingWindows.add(this);
+ }
+
+ mTapExcludeRegionHolder.updateRegion(regionId, left, top, width, height);
+ // Trigger touch exclude region update on current display.
+ final boolean isAppFocusedOnDisplay = mService.mFocusedApp != null
+ && mService.mFocusedApp.getDisplayContent() == currentDisplay;
+ currentDisplay.setTouchExcludeRegion(isAppFocusedOnDisplay ? mService.mFocusedApp.getTask()
+ : null);
+ }
+
+ /** Union the region with current tap exclude region that this window provides. */
+ void amendTapExcludeRegion(Region region) {
+ mTapExcludeRegionHolder.amendRegion(region, getBounds());
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;