Merge "GestureNav: Limit exclusion rects" into qt-dev
am: ea3216d462
Change-Id: Id52bcbd80028ce0030b7f8b38058e7a786744497
diff --git a/api/test-current.txt b/api/test-current.txt
index 931cafa..d2dbacb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2295,6 +2295,7 @@
field public static final String NAMESPACE_PRIVACY = "privacy";
field public static final String NAMESPACE_ROLLBACK = "rollback";
field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
+ field public static final String NAMESPACE_WINDOW_MANAGER = "android:window_manager";
}
public static interface DeviceConfig.OnPropertiesChangedListener {
@@ -2311,6 +2312,10 @@
method @Nullable public String getString(@NonNull String, @Nullable String);
}
+ public static interface DeviceConfig.WindowManager {
+ field public static final String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
+ }
+
public final class MediaStore {
method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static void deleteContributedMedia(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 19dbc6a..920eb4b 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -285,6 +285,15 @@
public static final String NAMESPACE_SETTINGS_UI = "settings_ui";
/**
+ * Namespace for window manager related features. The names to access the properties in this
+ * namespace should be defined in {@link WindowManager}.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String NAMESPACE_WINDOW_MANAGER = "android:window_manager";
+
+ /**
* List of namespaces which can be read without READ_DEVICE_CONFIG permission
*
* @hide
@@ -301,6 +310,23 @@
@TestApi
public static final String NAMESPACE_PRIVACY = "privacy";
+ /**
+ * Interface for accessing keys belonging to {@link #NAMESPACE_WINDOW_MANAGER}.
+ * @hide
+ */
+ @TestApi
+ public interface WindowManager {
+
+ /**
+ * Key for accessing the system gesture exclusion limit (an integer in dp).
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_WINDOW_MANAGER
+ * @hide
+ */
+ @TestApi
+ String KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP = "system_gesture_exclusion_limit_dp";
+ }
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertyChangedListener, Pair<String, Executor>> sSingleListeners =
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c3a769b..f548878 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -30,16 +30,21 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_LEFT_GESTURES;
+import static android.view.InsetsState.TYPE_RIGHT_GESTURES;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
+import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_TOP;
@@ -135,6 +140,7 @@
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.utils.RegionUtils.forEachRect;
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
import android.animation.AnimationHandler;
@@ -324,6 +330,7 @@
private final RemoteCallbackList<ISystemGestureExclusionListener>
mSystemGestureExclusionListeners = new RemoteCallbackList<>();
private final Region mSystemGestureExclusion = new Region();
+ private int mSystemGestureExclusionLimit;
/**
* For default display it contains real metrics, empty for others.
@@ -894,6 +901,8 @@
mWallpaperController = new WallpaperController(mWmService, this);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
+ mSystemGestureExclusionLimit = mWmService.mSystemGestureExclusionLimitDp
+ * mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
@@ -1549,8 +1558,8 @@
longSize = height;
}
- final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
- final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity;
+ final int shortSizeDp = shortSize * DENSITY_DEFAULT / mBaseDisplayDensity;
+ final int longSizeDp = longSize * DENSITY_DEFAULT / mBaseDisplayDensity;
mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
@@ -2200,6 +2209,18 @@
onDisplayChanged(this);
}
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ super.onDisplayChanged(dc);
+ updateSystemGestureExclusionLimit();
+ }
+
+ void updateSystemGestureExclusionLimit() {
+ mSystemGestureExclusionLimit = mWmService.mSystemGestureExclusionLimitDp
+ * mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
+ updateSystemGestureExclusion();
+ }
+
void initializeDisplayBaseInfo() {
final DisplayManagerInternal displayManagerInternal = mWmService.mDisplayManagerInternal;
if (displayManagerInternal != null) {
@@ -5110,24 +5131,35 @@
@VisibleForTesting
Region calculateSystemGestureExclusion() {
+ final Region unhandled = Region.obtain();
+ unhandled.set(0, 0, mDisplayFrames.mDisplayWidth, mDisplayFrames.mDisplayHeight);
+
+ final Rect leftEdge = mInsetsStateController.getSourceProvider(TYPE_LEFT_GESTURES)
+ .getSource().getFrame();
+ final Rect rightEdge = mInsetsStateController.getSourceProvider(TYPE_RIGHT_GESTURES)
+ .getSource().getFrame();
+
final Region global = Region.obtain();
final Region touchableRegion = Region.obtain();
final Region local = Region.obtain();
+ final int[] remainingLeftRight =
+ {mSystemGestureExclusionLimit, mSystemGestureExclusionLimit};
// Traverse all windows bottom up to assemble the gesture exclusion rects.
// For each window, we only take the rects that fall within its touchable region.
forAllWindows(w -> {
if (w.cantReceiveTouchInput() || !w.isVisible()
- || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0) {
+ || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0
+ || unhandled.isEmpty()) {
return;
}
final boolean modal =
(w.mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
- // Only keep the exclusion zones from the windows behind where the current window
- // isn't touchable.
+ // Get the touchable region of the window, and intersect with where the screen is still
+ // touchable, i.e. touchable regions on top are not covering it yet.
w.getTouchableRegion(touchableRegion);
- global.op(touchableRegion, Op.DIFFERENCE);
+ touchableRegion.op(unhandled, Op.INTERSECT);
rectListToRegion(w.getSystemGestureExclusion(), local);
@@ -5139,13 +5171,78 @@
// A window can only exclude system gestures where it is actually touchable
local.op(touchableRegion, Op.INTERSECT);
- global.op(local, Op.UNION);
- }, false /* topToBottom */);
+ // Apply restriction if necessary.
+ if (needsGestureExclusionRestrictions(w, mLastDispatchedSystemUiVisibility)) {
+
+ // Processes the region along the left edge.
+ remainingLeftRight[0] = addToGlobalAndConsumeLimit(local, global, leftEdge,
+ remainingLeftRight[0]);
+
+ // Processes the region along the right edge.
+ remainingLeftRight[1] = addToGlobalAndConsumeLimit(local, global, rightEdge,
+ remainingLeftRight[1]);
+
+ // Adds the middle (unrestricted area)
+ final Region middle = Region.obtain(local);
+ middle.op(leftEdge, Op.DIFFERENCE);
+ middle.op(rightEdge, Op.DIFFERENCE);
+ global.op(middle, Op.UNION);
+ middle.recycle();
+ } else {
+ global.op(local, Op.UNION);
+ }
+ unhandled.op(touchableRegion, Op.DIFFERENCE);
+ }, true /* topToBottom */);
local.recycle();
touchableRegion.recycle();
+ unhandled.recycle();
return global;
}
+ /**
+ * @return Whether gesture exclusion area should be restricted from the window depending on the
+ * current SystemUI visibility flags.
+ */
+ private static boolean needsGestureExclusionRestrictions(WindowState win, int sysUiVisibility) {
+ final int type = win.mAttrs.type;
+ final int stickyHideNavFlags =
+ SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ final boolean stickyHideNav =
+ (sysUiVisibility & stickyHideNavFlags) == stickyHideNavFlags;
+ return !stickyHideNav && type != TYPE_INPUT_METHOD && type != TYPE_STATUS_BAR
+ && win.getActivityType() != ACTIVITY_TYPE_HOME;
+ }
+
+ /**
+ * Adds a local gesture exclusion area to the global area while applying a limit per edge.
+ *
+ * @param local The gesture exclusion area to add.
+ * @param global The destination.
+ * @param edge Only processes the part in that region.
+ * @param limit How much limit in pixels we have.
+ * @return How much of the limit are remaining.
+ */
+ private static int addToGlobalAndConsumeLimit(Region local, Region global, Rect edge,
+ int limit) {
+ final Region r = Region.obtain(local);
+ r.op(edge, Op.INTERSECT);
+
+ final int[] remaining = {limit};
+ forEachRect(r, rect -> {
+ if (remaining[0] <= 0) {
+ return;
+ }
+ final int height = rect.height();
+ if (height > remaining[0]) {
+ rect.bottom = rect.top + remaining[0];
+ }
+ remaining[0] -= height;
+ global.op(rect, Op.UNION);
+ });
+ r.recycle();
+ return remaining[0];
+ }
+
void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener) {
mSystemGestureExclusionListeners.register(listener);
final boolean changed;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 28bb3d8..2db0131 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -32,6 +32,7 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -154,6 +155,7 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Looper;
@@ -175,6 +177,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -380,6 +383,8 @@
private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000;
+ private static final int MIN_GESTURE_EXCLUSION_LIMIT_DP = 200;
+
final WindowTracing mWindowTracing;
final private KeyguardDisableHandler mKeyguardDisableHandler;
@@ -838,6 +843,8 @@
final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
boolean mWindowsChanged = false;
+ int mSystemGestureExclusionLimitDp;
+
public interface WindowChangeListener {
public void windowsChanged();
public void focusChanged();
@@ -1132,6 +1139,21 @@
this, mInputManager, mActivityTaskManager, mH.getLooper());
mDragDropController = new DragDropController(this, mH.getLooper());
+ mSystemGestureExclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ new HandlerExecutor(mH), properties -> {
+ synchronized (mGlobalLock) {
+ final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
+ properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ if (mSystemGestureExclusionLimitDp != exclusionLimitDp) {
+ mSystemGestureExclusionLimitDp = exclusionLimitDp;
+ mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
+ }
+ }
+ });
+
LocalServices.addService(WindowManagerInternal.class, new LocalService());
}
diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java
index 1458440..8cd6f88 100644
--- a/services/core/java/com/android/server/wm/utils/RegionUtils.java
+++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java
@@ -18,8 +18,10 @@
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.RegionIterator;
import java.util.List;
+import java.util.function.Consumer;
/**
* Utility methods to handle Regions.
@@ -42,4 +44,18 @@
outRegion.union(rects.get(i));
}
}
+
+ /**
+ * Applies actions on each rect contained within a {@code Region}.
+ *
+ * @param region the given region.
+ * @param rectConsumer the action holder.
+ */
+ public static void forEachRect(Region region, Consumer<Rect> rectConsumer) {
+ final RegionIterator it = new RegionIterator(region);
+ final Rect rect = new Rect();
+ while (it.next(rect)) {
+ rectConsumer.accept(rect);
+ }
+ }
}
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 5136705..2ce33fc 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -36,6 +36,7 @@
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"