diff options
| author | 2019-02-07 20:45:11 +0100 | |
|---|---|---|
| committer | 2019-03-18 17:45:05 +0000 | |
| commit | 4ffc8977e2efbec660323b8972d5e35c7059aaa3 (patch) | |
| tree | 2efa0f4ee97ebea1ac3322db09ce30374881d169 | |
| parent | c00ee569661f99dc776007dae8290e4500c401cb (diff) | |
Gestures: Assemble per-window exclusion zones
Adds a facility to listen for per-window exclusion zones and
assembles them into a per-display exclusion zone ready for
consumption by interested parties.
Bug: 126360272
Test: atest DisplayContentTests
Change-Id: Ic186f86ca551af98a5f77beb254d257c281a4160
13 files changed, 397 insertions, 4 deletions
diff --git a/Android.bp b/Android.bp index e8f35611e74b..fe9757fdf496 100644 --- a/Android.bp +++ b/Android.bp @@ -383,6 +383,7 @@ java_defaults { "core/java/android/view/IRecentsAnimationRunner.aidl", "core/java/android/view/IRemoteAnimationFinishedCallback.aidl", "core/java/android/view/IRotationWatcher.aidl", + "core/java/android/view/ISystemGestureExclusionListener.aidl", "core/java/android/view/IWallpaperVisibilityListener.aidl", "core/java/android/view/IWindow.aidl", "core/java/android/view/IWindowFocusObserver.aidl", diff --git a/core/java/android/view/ISystemGestureExclusionListener.aidl b/core/java/android/view/ISystemGestureExclusionListener.aidl new file mode 100644 index 000000000000..a032625547d2 --- /dev/null +++ b/core/java/android/view/ISystemGestureExclusionListener.aidl @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, 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 android.view; + +import android.graphics.Region; + +/** + * Listener for changes to the system gesture exclusion region + * + * {@hide} + */ +oneway interface ISystemGestureExclusionListener { + /** + * Called when the system gesture exclusion for the given display changed. + * @param displayId the display whose system gesture exclusion changed + * @param systemGestureExclusion a {@code Region} where the app would like priority over the + * system gestures, in display coordinates. + */ + void onSystemGestureExclusionChanged(int displayId, in Region systemGestureExclusion); +}
\ No newline at end of file diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index e32c4e136a6e..42f7d9e50555 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -40,6 +40,7 @@ import android.view.IOnKeyguardExitResult; import android.view.IPinnedStackListener; import android.view.RemoteAnimationAdapter; import android.view.IRotationWatcher; +import android.view.ISystemGestureExclusionListener; import android.view.IWallpaperVisibilityListener; import android.view.IWindowSession; import android.view.IWindowSessionCallback; @@ -281,6 +282,18 @@ interface IWindowManager int displayId); /** + * Registers a system gesture exclusion listener for a given display. + */ + void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener, + int displayId); + + /** + * Unregisters a system gesture exclusion listener for a given display. + */ + void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener, + int displayId); + + /** * Used only for assist -- request a screenshot of the current application. */ boolean requestAssistScreenshot(IAssistDataReceiver receiver); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 1fcd4321cdde..87efb3fbf6c0 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -32,6 +32,8 @@ import android.view.InsetsState; import android.view.Surface; import android.view.SurfaceControl; +import java.util.List; + /** * System private per-application interface to the window manager. * @@ -265,4 +267,10 @@ interface IWindowSession { * that new state. */ void insetsModified(IWindow window, in InsetsState state); + + + /** + * Called when the system gesture exclusion has changed. + */ + void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9222bd62f1c4..14df87c39ae1 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3990,7 +3990,11 @@ public final class ViewRootImpl implements ViewParent, void systemGestureExclusionChanged() { final List<Rect> rectsForWindowManager = mGestureExclusionTracker.computeChangedRects(); if (rectsForWindowManager != null) { - // TODO Send to WM + try { + mWindowSession.reportSystemGestureExclusionChanged(mWindow, rectsForWindowManager); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } mAttachInfo.mTreeObserver .dispatchOnSystemGestureExclusionRectsChanged(rectsForWindowManager); } diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index 4773e168fffa..3205b5aef281 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -20,12 +20,16 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.FontMetricsInt; +import android.graphics.Path; import android.graphics.RectF; +import android.graphics.Region; import android.hardware.input.InputManager; import android.hardware.input.InputManager.InputDeviceListener; +import android.os.RemoteException; import android.os.SystemProperties; import android.util.Log; import android.util.Slog; +import android.view.ISystemGestureExclusionListener; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; @@ -34,6 +38,7 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.WindowInsets; +import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicyConstants.PointerEventListener; import java.util.ArrayList; @@ -124,12 +129,16 @@ public class PointerLocationView extends View implements InputDeviceListener, private int mActivePointerId; private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>(); private final PointerCoords mTempCoords = new PointerCoords(); - + + private final Region mSystemGestureExclusion = new Region(); + private final Path mSystemGestureExclusionPath = new Path(); + private final Paint mSystemGestureExclusionPaint; + private final VelocityTracker mVelocity; private final VelocityTracker mAltVelocity; private final FasterStringBuilder mText = new FasterStringBuilder(); - + private boolean mPrintCoords = true; public PointerLocationView(Context c) { @@ -168,7 +177,11 @@ public class PointerLocationView extends View implements InputDeviceListener, mPathPaint.setARGB(255, 0, 96, 255); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(1); - + + mSystemGestureExclusionPaint = new Paint(); + mSystemGestureExclusionPaint.setARGB(25, 255, 0, 0); + mSystemGestureExclusionPaint.setStyle(Paint.Style.FILL_AND_STROKE); + PointerState ps = new PointerState(); mPointers.add(ps); mActivePointerId = 0; @@ -236,6 +249,12 @@ public class PointerLocationView extends View implements InputDeviceListener, final int NP = mPointers.size(); + if (!mSystemGestureExclusion.isEmpty()) { + mSystemGestureExclusionPath.reset(); + mSystemGestureExclusion.getBoundaryPath(mSystemGestureExclusionPath); + canvas.drawPath(mSystemGestureExclusionPath, mSystemGestureExclusionPaint); + } + // Labels if (mActivePointerId >= 0) { final PointerState ps = mPointers.get(mActivePointerId); @@ -719,6 +738,12 @@ public class PointerLocationView extends View implements InputDeviceListener, super.onAttachedToWindow(); mIm.registerInputDeviceListener(this, getHandler()); + try { + WindowManagerGlobal.getWindowManagerService().registerSystemGestureExclusionListener( + mSystemGestureExclusionListener, mContext.getDisplayId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } logInputDevices(); } @@ -727,6 +752,12 @@ public class PointerLocationView extends View implements InputDeviceListener, super.onDetachedFromWindow(); mIm.unregisterInputDeviceListener(this); + try { + WindowManagerGlobal.getWindowManagerService().unregisterSystemGestureExclusionListener( + mSystemGestureExclusionListener, mContext.getDisplayId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } @Override @@ -876,4 +907,17 @@ public class PointerLocationView extends View implements InputDeviceListener, return oldLength; } } + + private ISystemGestureExclusionListener mSystemGestureExclusionListener = + new ISystemGestureExclusionListener.Stub() { + @Override + public void onSystemGestureExclusionChanged(int displayId, Region systemGestureExclusion) { + Region exclusion = Region.obtain(systemGestureExclusion); + getHandler().post(() -> { + mSystemGestureExclusion.set(exclusion); + exclusion.recycle(); + invalidate(); + }); + } + }; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index db590308460b..b685a6327ae2 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -128,6 +128,7 @@ import static com.android.server.wm.WindowManagerService.logSurface; 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.rectListToRegion; import android.animation.AnimationHandler; import android.annotation.CallSuper; @@ -151,6 +152,7 @@ import android.os.Debug; import android.os.Handler; import android.os.IBinder; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -163,6 +165,7 @@ import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; +import android.view.ISystemGestureExclusionListener; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputWindowHandle; @@ -308,6 +311,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private DisplayRotation mDisplayRotation; DisplayFrames mDisplayFrames; + private final RemoteCallbackList<ISystemGestureExclusionListener> + mSystemGestureExclusionListeners = new RemoteCallbackList<>(); + private final Region mSystemGestureExclusion = new Region(); + /** * For default display it contains real metrics, empty for others. * @see WindowManagerService#createWatermarkInTransaction() @@ -2789,6 +2796,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mWallpaperController.dump(pw, " "); pw.println(); + pw.print("mSystemGestureExclusion="); + if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() > 0) { + pw.println(mSystemGestureExclusion); + } else { + pw.println("<no lstnrs>"); + } + + pw.println(); pw.println(prefix + "Application tokens in top down Z order:"); for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) { final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx); @@ -4914,6 +4929,100 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } /** + * Updates the display's system gesture exclusion. + * + * @return true, if the exclusion changed. + */ + boolean updateSystemGestureExclusion() { + if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() == 0) { + // No one's interested anyways. + return false; + } + + final Region systemGestureExclusion = calculateSystemGestureExclusion(); + try { + if (mSystemGestureExclusion.equals(systemGestureExclusion)) { + return false; + } + mSystemGestureExclusion.set(systemGestureExclusion); + for (int i = mSystemGestureExclusionListeners.beginBroadcast() - 1; i >= 0; --i) { + try { + mSystemGestureExclusionListeners.getBroadcastItem(i) + .onSystemGestureExclusionChanged(mDisplayId, systemGestureExclusion); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to notify SystemGestureExclusionListener", e); + } + } + mSystemGestureExclusionListeners.finishBroadcast(); + return true; + } finally { + systemGestureExclusion.recycle(); + } + } + + @VisibleForTesting + Region calculateSystemGestureExclusion() { + final Region global = Region.obtain(); + final Region touchableRegion = Region.obtain(); + final Region local = Region.obtain(); + + // 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) { + 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. + w.getTouchableRegion(touchableRegion); + global.op(touchableRegion, Op.DIFFERENCE); + + rectListToRegion(w.getSystemGestureExclusion(), local); + + // Transform to display coordinates + local.scale(w.mGlobalScale); + final Rect frame = w.getWindowFrames().mFrame; + local.translate(frame.left, frame.top); + + // A window can only exclude system gestures where it is actually touchable + local.op(touchableRegion, Op.INTERSECT); + + global.op(local, Op.UNION); + }, false /* topToBottom */); + local.recycle(); + touchableRegion.recycle(); + return global; + } + + void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener) { + mSystemGestureExclusionListeners.register(listener); + final boolean changed; + if (mSystemGestureExclusionListeners.getRegisteredCallbackCount() == 1) { + changed = updateSystemGestureExclusion(); + } else { + changed = false; + } + + if (!changed) { + // If updateSystemGestureExclusion changed the exclusion, it will already have + // notified the listener. Otherwise, we'll do it here. + try { + listener.onSystemGestureExclusionChanged(mDisplayId, mSystemGestureExclusion); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to notify SystemGestureExclusionListener during register", e); + } + } + } + + void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener) { + mSystemGestureExclusionListeners.unregister(listener); + } + + /** * Create a portal window handle for input. This window transports any touch to the display * indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window. * diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 300cd17f33dd..1167d6f1d615 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -654,6 +654,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Finally update all input windows now that the window changes have stabilized. forAllDisplays(dc -> { dc.getInputMonitor().updateInputWindowsLw(true /*force*/); + dc.updateSystemGestureExclusion(); }); mWmService.setHoldScreenLocked(mHoldScreen); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index dc8c7b79feef..9b634f959fca 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -57,6 +57,7 @@ import com.android.server.wm.WindowManagerService.H; import java.io.PrintWriter; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.function.BiConsumer; @@ -314,6 +315,16 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } } + @Override + public void reportSystemGestureExclusionChanged(IWindow window, List<Rect> exclusionRects) { + long ident = Binder.clearCallingIdentity(); + try { + mService.reportSystemGestureExclusionChanged(this, window, exclusionRects); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + private void actionOnWallpaper(IBinder window, BiConsumer<WallpaperController, WindowState> action) { final WindowState windowState = mService.windowForClientLocked(this, window, true); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e3a8be5d52d4..a1e6eea39f86 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -202,6 +202,7 @@ import android.view.IOnKeyguardExitResult; import android.view.IPinnedStackListener; import android.view.IRecentsAnimationRunner; import android.view.IRotationWatcher; +import android.view.ISystemGestureExclusionListener; import android.view.IWallpaperVisibilityListener; import android.view.IWindow; import android.view.IWindowId; @@ -274,6 +275,7 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.List; /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub @@ -3788,6 +3790,42 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener, + int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException("Trying to register visibility event " + + "for invalid display: " + displayId); + } + displayContent.registerSystemGestureExclusionListener(listener); + } + } + + @Override + public void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener, + int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent == null) { + throw new IllegalArgumentException("Trying to register visibility event " + + "for invalid display: " + displayId); + } + displayContent.unregisterSystemGestureExclusionListener(listener); + } + } + + void reportSystemGestureExclusionChanged(Session session, IWindow window, + List<Rect> exclusionRects) { + synchronized (mGlobalLock) { + final WindowState win = windowForClientLocked(session, window, true); + if (win.setSystemGestureExclusion(exclusionRects)) { + win.getDisplayContent().updateSystemGestureExclusion(); + } + } + } + + @Override public void registerDisplayFoldListener(IDisplayFoldListener listener) { mPolicy.registerDisplayFoldListener(listener); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 20cca66d8a52..dd9e5901dc7a 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -205,6 +205,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Comparator; +import java.util.List; import java.util.function.Predicate; /** A window in the window manager. */ @@ -363,6 +364,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private final Rect mInsetFrame = new Rect(); + /** + * List of rects where system gestures should be ignored. + * + * Coordinates are relative to the window's position. + */ + private final List<Rect> mExclusionRects = new ArrayList<>(); + // If a window showing a wallpaper: the requested offset for the // wallpaper; if a wallpaper window: the currently applied offset. float mWallpaperX = -1; @@ -612,6 +620,24 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } + List<Rect> getSystemGestureExclusion() { + return mExclusionRects; + } + + /** + * Sets the system gesture exclusion rects. + * + * @return {@code true} if anything changed + */ + boolean setSystemGestureExclusion(List<Rect> exclusionRects) { + if (mExclusionRects.equals(exclusionRects)) { + return false; + } + mExclusionRects.clear(); + mExclusionRects.addAll(exclusionRects); + return true; + } + interface PowerManagerWrapper { void wakeUp(long time, @WakeReason int reason, String details); diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java new file mode 100644 index 000000000000..1458440f7b81 --- /dev/null +++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 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.utils; + +import android.graphics.Rect; +import android.graphics.Region; + +import java.util.List; + +/** + * Utility methods to handle Regions. + */ +public class RegionUtils { + + private RegionUtils() {} + + + /** + * Converts a list of rects into a {@code Region}. + * + * @param rects the list of rects to convert + * @param outRegion the Region to set to the list of rects + */ + public static void rectListToRegion(List<Rect> rects, Region outRegion) { + outRegion.setEmpty(); + final int n = rects.size(); + for (int i = 0; i < n; i++) { + outRegion.union(rects.get(i)); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index b26aa050870a..51dac6fdd1eb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -25,6 +25,8 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -57,12 +59,15 @@ import android.annotation.SuppressLint; import android.app.WindowConfiguration; import android.content.res.Configuration; import android.graphics.Rect; +import android.graphics.Region; import android.metrics.LogMaker; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; +import android.util.MutableBoolean; import android.view.DisplayCutout; import android.view.Gravity; +import android.view.ISystemGestureExclusionListener; import android.view.MotionEvent; import android.view.Surface; import android.view.ViewRootImpl; @@ -673,6 +678,60 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testUpdateSystemGestureExclusion() throws Exception { + final DisplayContent dc = createNewDisplay(); + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); + win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40))); + + dc.setLayoutNeeded(); + dc.performLayout(true /* initial */, false /* updateImeWindows */); + + win.setHasSurface(true); + dc.updateSystemGestureExclusion(); + + final MutableBoolean invoked = new MutableBoolean(false); + final ISystemGestureExclusionListener.Stub verifier = + new ISystemGestureExclusionListener.Stub() { + @Override + public void onSystemGestureExclusionChanged(int displayId, Region actual) { + Region expected = Region.obtain(); + expected.set(10, 20, 30, 40); + assertEquals(expected, actual); + invoked.value = true; + } + }; + try { + dc.registerSystemGestureExclusionListener(verifier); + } finally { + dc.unregisterSystemGestureExclusionListener(verifier); + } + assertTrue("SystemGestureExclusionListener was not invoked", invoked.value); + } + + @Test + public void testCalculateSystemGestureExclusion() throws Exception { + final DisplayContent dc = createNewDisplay(); + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); + win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40))); + + final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "win2"); + win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50))); + + dc.setLayoutNeeded(); + dc.performLayout(true /* initial */, false /* updateImeWindows */); + + win.setHasSurface(true); + win2.setHasSurface(true); + + final Region expected = Region.obtain(); + expected.set(20, 30, 40, 50); + assertEquals(expected, dc.calculateSystemGestureExclusion()); + } + + @Test public void testOrientationChangeLogging() { MetricsLogger mockLogger = mock(MetricsLogger.class); Configuration oldConfig = new Configuration(); |