From 5bf63371a180a15d3bcb48947d7aefb55b864519 Mon Sep 17 00:00:00 2001 From: Jorge Gil Date: Wed, 6 Nov 2024 20:33:56 +0000 Subject: Replace custom header spy window with custom touchable regions When custom headers are being used, even though input falls through the spy window into the app's customized header, a11y isn't able to focus the app's views when they're fully "occluded" (even though it's transparent) by the spy window. As an alternative approach to handling input, this CL removes the spy input feature and instead explicitly sets touchable regions of the caption bar to not included the customized space. This keeps the existing input requirements the same (customized region in app receives input, system regions and left over non-exclusion space is used by the system for header buttons or drag-moving), but also allows the a11y service to focus the app's views. This is enabled behind a flag/param as it touches a lot of input handling code. It also requires calling #relayout on exclusion region changes because it's needed to update the touchable region of the window now. Flag: com.android.window.flags.enable_accessible_custom_headers Fix: 339302584 Test: manual - using Clank with tabs in the custom header, check talkback is able to focus the tabs and their X button. Change-Id: I7ca24a71c4113e352ea173ac97ecb6e20bbbecb9 --- .../windowdecor/CaptionWindowDecorViewModel.java | 23 ++- .../shell/windowdecor/CaptionWindowDecoration.java | 16 +- .../DesktopModeWindowDecorViewModel.java | 53 ++++-- .../windowdecor/DesktopModeWindowDecoration.java | 47 ++++-- .../wm/shell/windowdecor/WindowDecoration.java | 112 +++++++++++-- .../windowdecor/CaptionWindowDecorationTests.kt | 13 +- .../DesktopModeWindowDecorViewModelTests.kt | 68 +++++++- .../DesktopModeWindowDecorationTests.java | 180 ++++++++++++++++----- .../shell/windowdecor/WindowDecorationTests.java | 28 ++-- 9 files changed, 426 insertions(+), 114 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 7265fb8f8027..c9f2d2e8c0e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions; +import android.annotation.NonNull; import android.app.ActivityManager.RunningTaskInfo; import android.content.ContentResolver; import android.content.Context; @@ -110,6 +111,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT } mMainExecutor.execute(() -> { mExclusionRegion.set(systemGestureExclusion); + onExclusionRegionChanged(displayId, mExclusionRegion); }); } }; @@ -163,7 +165,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT boolean isFocusedGlobally) { final WindowDecoration decor = mWindowDecorByTaskId.get(taskId); if (decor != null) { - decor.relayout(decor.mTaskInfo, isFocusedGlobally); + decor.relayout(decor.mTaskInfo, isFocusedGlobally, decor.mExclusionRegion); } } @@ -199,9 +201,9 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT if (enableDisplayFocusInShellTransitions()) { // Pass the current global focus status to avoid updates outside of a ShellTransition. - decoration.relayout(taskInfo, decoration.mHasGlobalFocus); + decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion); } else { - decoration.relayout(taskInfo, taskInfo.isFocused); + decoration.relayout(taskInfo, taskInfo.isFocused, decoration.mExclusionRegion); } } @@ -240,7 +242,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT } else { decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion); } } @@ -254,7 +256,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion); } @Override @@ -266,6 +268,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT decoration.close(); } + private void onExclusionRegionChanged(int displayId, @NonNull Region exclusionRegion) { + final int decorCount = mWindowDecorByTaskId.size(); + for (int i = 0; i < decorCount; i++) { + final CaptionWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i); + if (decoration.mTaskInfo.displayId != displayId) continue; + decoration.onExclusionRegionChanged(exclusionRegion); + } + } + private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { return true; @@ -333,7 +344,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT windowDecoration.setTaskDragResizer(taskPositioner); windowDecoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion); } private class CaptionTouchEventListener implements diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index c9546731a193..982fda0ddf36 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -36,6 +36,7 @@ import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.graphics.drawable.GradientDrawable; import android.os.Handler; import android.util.Size; @@ -174,7 +175,8 @@ public class CaptionWindowDecoration extends WindowDecoration { mExclusionRegion.set(systemGestureExclusion); + onExclusionRegionChanged(displayId, mExclusionRegion); }); } }; @@ -432,7 +433,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, boolean isFocusedGlobally) { final WindowDecoration decor = mWindowDecorByTaskId.get(taskId); if (decor != null) { - decor.relayout(decor.mTaskInfo, isFocusedGlobally); + decor.relayout(decor.mTaskInfo, isFocusedGlobally, decor.mExclusionRegion); } } @@ -471,9 +472,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } if (enableDisplayFocusInShellTransitions()) { // Pass the current global focus status to avoid updates outside of a ShellTransition. - decoration.relayout(taskInfo, decoration.mHasGlobalFocus); + decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion); } else { - decoration.relayout(taskInfo, taskInfo.isFocused); + decoration.relayout(taskInfo, taskInfo.isFocused, decoration.mExclusionRegion); } mActivityOrientationChangeHandler.ifPresent(handler -> handler.handleActivityOrientationChange(oldTaskInfo, taskInfo)); @@ -514,7 +515,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } else { decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), + mExclusionRegion); } } @@ -528,7 +530,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), + mExclusionRegion); } @Override @@ -548,6 +551,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mWindowDecorByTaskId.remove(taskInfo.taskId); } + private void onExclusionRegionChanged(int displayId, @NonNull Region exclusionRegion) { + final int decorCount = mWindowDecorByTaskId.size(); + for (int i = 0; i < decorCount; i++) { + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i); + if (decoration.mTaskInfo.displayId != displayId) continue; + decoration.onExclusionRegionChanged(exclusionRegion); + } + } + private void openHandleMenu(int taskId) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId); decoration.createHandleMenu(checkNumberOfOtherInstances(decoration.mTaskInfo) @@ -750,10 +762,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, /** * Whether to pilfer the next motion event to send cancellations to the windows below. - * Useful when the caption window is spy and the gesture should be handle by the system + * Useful when the caption window is spy and the gesture should be handled by the system * instead of by the app for their custom header content. + * Should not have any effect when {@link Flags#enableAccessibleCustomHeaders()}, because + * a spy window is not used then. */ - private boolean mShouldPilferCaptionEvents; + private boolean mIsCustomHeaderGesture; + private boolean mIsResizeGesture; private boolean mIsDragging; private boolean mTouchscreenInUse; private boolean mHasLongClicked; @@ -867,7 +882,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, // to offset position relative to caption as a whole. int[] viewLocation = new int[2]; v.getLocationInWindow(viewLocation); - final boolean isResizeEvent = decoration.shouldResizeListenerHandleEvent(e, + mIsResizeGesture = decoration.shouldResizeListenerHandleEvent(e, new Point(viewLocation[0], viewLocation[1])); // The caption window may be a spy window when the caption background is // transparent, which means events will fall through to the app window. Make @@ -875,21 +890,23 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, // customizable region and what the app reported as exclusion areas, because // the drag-move or other caption gestures should take priority outside those // regions. - mShouldPilferCaptionEvents = !(downInCustomizableCaptionRegion - && downInExclusionRegion && isTransparentCaption) && !isResizeEvent; + mIsCustomHeaderGesture = downInCustomizableCaptionRegion + && downInExclusionRegion && isTransparentCaption; } - if (!mShouldPilferCaptionEvents) { - // The event will be handled by a window below or pilfered by resize handler. + if (mIsCustomHeaderGesture || mIsResizeGesture) { + // The event will be handled by the custom window below or pilfered by resize + // handler. return false; } - // Otherwise pilfer so that windows below receive cancellations for this gesture, and - // continue normal handling as a caption gesture. - if (mInputManager != null) { + if (mInputManager != null + && !Flags.enableAccessibleCustomHeaders()) { + // Pilfer so that windows below receive cancellations for this gesture. mInputManager.pilferPointers(v.getViewRootImpl().getInputToken()); } if (isUpOrCancel) { // Gesture is finished, reset state. - mShouldPilferCaptionEvents = false; + mIsCustomHeaderGesture = false; + mIsResizeGesture = false; } if (isAppHandle) { return mHandleDragDetector.onMotionEvent(v, e); @@ -1592,7 +1609,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, windowDecoration.setDragPositioningCallback(taskPositioner); windowDecoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), + mExclusionRegion); if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { incrementEventReceiverTasks(taskInfo.displayId); } @@ -1618,6 +1636,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, pw.println(innerPrefix + "mTransitionDragActive=" + mTransitionDragActive); pw.println(innerPrefix + "mEventReceiversByDisplay=" + mEventReceiversByDisplay); pw.println(innerPrefix + "mWindowDecorByTaskId=" + mWindowDecorByTaskId); + pw.println(innerPrefix + "mExclusionRegion=" + mExclusionRegion); } private class DesktopModeOnTaskRepositionAnimationListener diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index d3772325004d..1e0054b7aaf4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -394,7 +394,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration show its header in sync since it's an integral part // of the window itself - a delayed header might cause bad UX. relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw, - shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion); } else { // The Task is outside Freeform mode -> allow the handle view to be delayed since the // handle is just a small addition to the window. relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw, - shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion); } Trace.endSection(); } @@ -462,11 +463,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration } mDisplayController.removeDisplayWindowListener(this); - relayout(mTaskInfo, mHasGlobalFocus); + relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion); } }; @@ -143,7 +143,7 @@ public abstract class WindowDecoration SurfaceControl mDecorationContainerSurface; SurfaceControl mCaptionContainerSurface; - private WindowlessWindowManager mCaptionWindowManager; + private CaptionWindowlessWindowManager mCaptionWindowManager; private SurfaceControlViewHost mViewHost; private Configuration mWindowDecorConfig; TaskDragResizer mTaskDragResizer; @@ -152,6 +152,7 @@ public abstract class WindowDecoration boolean mIsStatusBarVisible; boolean mIsKeyguardVisibleAndOccluded; boolean mHasGlobalFocus; + final Region mExclusionRegion = Region.obtain(); /** The most recent set of insets applied to this window decoration. */ private WindowDecorationInsets mWindowDecorationInsets; @@ -218,7 +219,8 @@ public abstract class WindowDecoration * constructor. * @param hasGlobalFocus Whether the task is focused */ - abstract void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus); + abstract void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion); /** * Used by the {@link DragPositioningCallback} associated with the implementing class to @@ -244,6 +246,7 @@ public abstract class WindowDecoration mTaskInfo = params.mRunningTaskInfo; } mHasGlobalFocus = params.mHasGlobalFocus; + mExclusionRegion.set(params.mDisplayExclusionRegion); final int oldLayoutResId = mLayoutResId; mLayoutResId = params.mLayoutResId; @@ -402,7 +405,7 @@ public abstract class WindowDecoration final int elementWidthPx = resources.getDimensionPixelSize(element.mWidthResId); boundingRects[i] = - calculateBoundingRect(element, elementWidthPx, captionInsetsRect); + calculateBoundingRectLocal(element, elementWidthPx, captionInsetsRect); // Subtract the regions used by the caption elements, the rest is // customizable. if (params.hasInputFeatureSpy()) { @@ -477,9 +480,8 @@ public abstract class WindowDecoration if (mCaptionWindowManager == null) { // Put caption under a container surface because ViewRootImpl sets the destination frame // of windowless window layers and BLASTBufferQueue#update() doesn't support offset. - mCaptionWindowManager = new WindowlessWindowManager( - mTaskInfo.getConfiguration(), mCaptionContainerSurface, - null /* hostInputToken */); + mCaptionWindowManager = new CaptionWindowlessWindowManager( + mTaskInfo.getConfiguration(), mCaptionContainerSurface); } mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration()); final WindowManager.LayoutParams lp = @@ -492,6 +494,14 @@ public abstract class WindowDecoration lp.setTitle("Caption of Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); lp.inputFeatures = params.mInputFeatures; + final Rect localCaptionBounds = new Rect( + outResult.mCaptionX, + outResult.mCaptionY, + outResult.mCaptionX + outResult.mCaptionWidth, + outResult.mCaptionY + outResult.mCaptionHeight); + final Region touchableRegion = params.mLimitTouchRegionToSystemAreas + ? calculateLimitedTouchableRegion(params, localCaptionBounds) + : null; if (mViewHost == null) { Trace.beginSection("CaptionViewHostLayout-new"); mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay, @@ -503,6 +513,9 @@ public abstract class WindowDecoration mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0); + if (params.mLimitTouchRegionToSystemAreas) { + mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion); + } mViewHost.setView(outResult.mRootView, lp); Trace.endSection(); } else { @@ -514,13 +527,71 @@ public abstract class WindowDecoration mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0); + if (params.mLimitTouchRegionToSystemAreas) { + mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion); + } mViewHost.relayout(lp); Trace.endSection(); } + if (touchableRegion != null) { + touchableRegion.recycle(); + } Trace.endSection(); // CaptionViewHostLayout } - private Rect calculateBoundingRect(@NonNull OccludingCaptionElement element, + @NonNull + private Region calculateLimitedTouchableRegion( + RelayoutParams params, + @NonNull Rect localCaptionBounds) { + // Make caption bounds relative to display to align with exclusion region. + final Point positionInParent = params.mRunningTaskInfo.positionInParent; + final Rect captionBoundsInDisplay = new Rect(localCaptionBounds); + captionBoundsInDisplay.offsetTo(positionInParent.x, positionInParent.y); + + final Region boundingRects = calculateBoundingRectsRegion(params, captionBoundsInDisplay); + + final Region customizedRegion = Region.obtain(); + customizedRegion.set(captionBoundsInDisplay); + customizedRegion.op(boundingRects, Region.Op.DIFFERENCE); + customizedRegion.op(params.mDisplayExclusionRegion, Region.Op.INTERSECT); + + final Region touchableRegion = Region.obtain(); + touchableRegion.set(captionBoundsInDisplay); + touchableRegion.op(customizedRegion, Region.Op.DIFFERENCE); + // Return resulting region back to window coordinates. + touchableRegion.translate(-positionInParent.x, -positionInParent.y); + + boundingRects.recycle(); + customizedRegion.recycle(); + return touchableRegion; + } + + @NonNull + private Region calculateBoundingRectsRegion( + @NonNull RelayoutParams params, + @NonNull Rect captionBoundsInDisplay) { + final int numOfElements = params.mOccludingCaptionElements.size(); + final Region region = Region.obtain(); + if (numOfElements == 0) { + // The entire caption is a bounding rect. + region.set(captionBoundsInDisplay); + return region; + } + final Resources resources = mDecorWindowContext.getResources(); + for (int i = 0; i < numOfElements; i++) { + final OccludingCaptionElement element = params.mOccludingCaptionElements.get(i); + final int elementWidthPx = resources.getDimensionPixelSize(element.mWidthResId); + final Rect boundingRect = calculateBoundingRectLocal(element, elementWidthPx, + captionBoundsInDisplay); + // Bounding rect is initially calculated relative to the caption, so offset it to make + // it relative to the display. + boundingRect.offset(captionBoundsInDisplay.left, captionBoundsInDisplay.top); + region.union(boundingRect); + } + return region; + } + + private Rect calculateBoundingRectLocal(@NonNull OccludingCaptionElement element, int elementWidthPx, @NonNull Rect captionRect) { switch (element.mAlignment) { case START -> { @@ -539,7 +610,7 @@ public abstract class WindowDecoration mIsKeyguardVisibleAndOccluded = visible && occluded; final boolean changed = prevVisAndOccluded != mIsKeyguardVisibleAndOccluded; if (changed) { - relayout(mTaskInfo, mHasGlobalFocus); + relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion); } } @@ -549,10 +620,14 @@ public abstract class WindowDecoration final boolean changed = prevStatusBarVisibility != mIsStatusBarVisible; if (changed) { - relayout(mTaskInfo, mHasGlobalFocus); + relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion); } } + void onExclusionRegionChanged(@NonNull Region exclusionRegion) { + relayout(mTaskInfo, mHasGlobalFocus, exclusionRegion); + } + /** * Update caption visibility state and views. */ @@ -751,9 +826,11 @@ public abstract class WindowDecoration int mCaptionHeightId; int mCaptionWidthId; final List mOccludingCaptionElements = new ArrayList<>(); + boolean mLimitTouchRegionToSystemAreas; int mInputFeatures; boolean mIsInsetSource = true; @InsetsSource.Flags int mInsetSourceFlags; + final Region mDisplayExclusionRegion = Region.obtain(); int mShadowRadiusId; int mCornerRadius; @@ -772,9 +849,11 @@ public abstract class WindowDecoration mCaptionHeightId = Resources.ID_NULL; mCaptionWidthId = Resources.ID_NULL; mOccludingCaptionElements.clear(); + mLimitTouchRegionToSystemAreas = false; mInputFeatures = 0; mIsInsetSource = true; mInsetSourceFlags = 0; + mDisplayExclusionRegion.setEmpty(); mShadowRadiusId = Resources.ID_NULL; mCornerRadius = 0; @@ -830,6 +909,19 @@ public abstract class WindowDecoration } } + private static class CaptionWindowlessWindowManager extends WindowlessWindowManager { + CaptionWindowlessWindowManager( + @NonNull Configuration configuration, + @NonNull SurfaceControl rootSurface) { + super(configuration, rootSurface, /* hostInputToken= */ null); + } + + /** Set the view host's touchable region. */ + void setTouchRegion(@NonNull SurfaceControlViewHost viewHost, @NonNull Region region) { + setTouchRegion(viewHost.getWindowToken().asBinder(), region); + } + } + @VisibleForTesting public interface SurfaceControlViewHostFactory { default SurfaceControlViewHost create(Context c, Display d, WindowlessWindowManager wmm) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt index 5ebf5170bf86..59141ca39487 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorationTests.kt @@ -19,6 +19,7 @@ package com.android.wm.shell.windowdecor import android.app.ActivityManager import android.app.WindowConfiguration import android.content.ComponentName +import android.graphics.Region import android.testing.AndroidTestingRunner import android.view.Display import android.view.InsetsState @@ -33,6 +34,9 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) class CaptionWindowDecorationTests : ShellTestCase() { + + private val exclusionRegion = Region.obtain() + @Test fun updateRelayoutParams_freeformAndTransparentAppearance_allowsInputFallthrough() { val taskInfo = createTaskInfo() @@ -50,7 +54,8 @@ class CaptionWindowDecorationTests : ShellTestCase() { true /* isStatusBarVisible */, false /* isKeyguardVisibleAndOccluded */, InsetsState(), - true /* hasGlobalFocus */ + true /* hasGlobalFocus */, + exclusionRegion ) Truth.assertThat(relayoutParams.hasInputFeatureSpy()).isTrue() @@ -72,7 +77,8 @@ class CaptionWindowDecorationTests : ShellTestCase() { true /* isStatusBarVisible */, false /* isKeyguardVisibleAndOccluded */, InsetsState(), - true /* hasGlobalFocus */ + true /* hasGlobalFocus */, + exclusionRegion ) Truth.assertThat(relayoutParams.hasInputFeatureSpy()).isFalse() @@ -90,7 +96,8 @@ class CaptionWindowDecorationTests : ShellTestCase() { true /* isStatusBarVisible */, false /* isKeyguardVisibleAndOccluded */, InsetsState(), - true /* hasGlobalFocus */ + true /* hasGlobalFocus */, + exclusionRegion ) Truth.assertThat(relayoutParams.mOccludingCaptionElements.size).isEqualTo(2) Truth.assertThat(relayoutParams.mOccludingCaptionElements[0].mAlignment).isEqualTo( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 956100d9bc03..be664f86e9f5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -30,6 +30,7 @@ import android.content.Intent import android.content.Intent.ACTION_MAIN import android.content.pm.ActivityInfo import android.graphics.Rect +import android.graphics.Region import android.hardware.display.DisplayManager import android.hardware.display.VirtualDisplay import android.hardware.input.InputManager @@ -48,6 +49,7 @@ import android.testing.TestableLooper.RunWithLooper import android.util.SparseArray import android.view.Choreographer import android.view.Display.DEFAULT_DISPLAY +import android.view.ISystemGestureExclusionListener import android.view.IWindowManager import android.view.InputChannel import android.view.InputMonitor @@ -84,7 +86,6 @@ import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.MultiInstanceHelper -import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler import com.android.wm.shell.desktopmode.DesktopModeEventLogger @@ -131,6 +132,7 @@ import org.mockito.Mockito.times import org.mockito.kotlin.KArgumentCaptor import org.mockito.kotlin.verify import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argThat import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doNothing @@ -175,7 +177,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockInputMonitorFactory: DesktopModeWindowDecorViewModel.InputMonitorFactory @Mock private lateinit var mockShellController: ShellController - @Mock private lateinit var mockShellExecutor: ShellExecutor + private val testShellExecutor = TestShellExecutor() @Mock private lateinit var mockAppHeaderViewHolderFactory: AppHeaderViewHolder.Factory @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler @@ -230,13 +232,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { spyContext = spy(mContext) doNothing().`when`(spyContext).startActivity(any()) - shellInit = ShellInit(mockShellExecutor) + shellInit = ShellInit(testShellExecutor) windowDecorByTaskIdSpy.clear() spyContext.addMockSystemService(InputManager::class.java, mockInputManager) desktopModeEventLogger = mock() desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( spyContext, - mockShellExecutor, + testShellExecutor, mockMainHandler, mockMainChoreographer, bgExecutor, @@ -1321,11 +1323,11 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { decoration.mHasGlobalFocus = true desktopModeWindowDecorViewModel.onTaskInfoChanged(task) - verify(decoration).relayout(task, true) + verify(decoration).relayout(eq(task), eq(true), anyOrNull()) decoration.mHasGlobalFocus = false desktopModeWindowDecorViewModel.onTaskInfoChanged(task) - verify(decoration).relayout(task, false) + verify(decoration).relayout(eq(task), eq(false), anyOrNull()) } @Test @@ -1342,17 +1344,66 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { task.isFocused = true desktopModeWindowDecorViewModel.onTaskInfoChanged(task) - verify(decoration).relayout(task, true) + verify(decoration).relayout(eq(task), eq(true), anyOrNull()) task.isFocused = false desktopModeWindowDecorViewModel.onTaskInfoChanged(task) - verify(decoration).relayout(task, false) + verify(decoration).relayout(eq(task), eq(false), anyOrNull()) + } + + @Test + fun testGestureExclusionChanged_updatesDecorations() { + val captor = argumentCaptor() + verify(mockWindowManager) + .registerSystemGestureExclusionListener(captor.capture(), eq(DEFAULT_DISPLAY)) + val task = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + displayId = DEFAULT_DISPLAY + ) + val task2 = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + displayId = DEFAULT_DISPLAY + ) + val newRegion = Region.obtain().apply { + set(Rect(0, 0, 1600, 80)) + } + + captor.firstValue.onSystemGestureExclusionChanged(DEFAULT_DISPLAY, newRegion, newRegion) + testShellExecutor.flushAll() + + verify(task).onExclusionRegionChanged(newRegion) + verify(task2).onExclusionRegionChanged(newRegion) + } + + @Test + fun testGestureExclusionChanged_otherDisplay_skipsDecorationUpdate() { + val captor = argumentCaptor() + verify(mockWindowManager) + .registerSystemGestureExclusionListener(captor.capture(), eq(DEFAULT_DISPLAY)) + val task = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + displayId = DEFAULT_DISPLAY + ) + val task2 = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + displayId = 2 + ) + val newRegion = Region.obtain().apply { + set(Rect(0, 0, 1600, 80)) + } + + captor.firstValue.onSystemGestureExclusionChanged(DEFAULT_DISPLAY, newRegion, newRegion) + testShellExecutor.flushAll() + + verify(task).onExclusionRegionChanged(newRegion) + verify(task2, never()).onExclusionRegionChanged(newRegion) } private fun createOpenTaskDecoration( @WindowingMode windowingMode: Int, taskSurface: SurfaceControl = SurfaceControl(), requestingImmersive: Boolean = false, + displayId: Int = DEFAULT_DISPLAY, onMaxOrRestoreListenerCaptor: ArgumentCaptor> = forClass(Function0::class.java) as ArgumentCaptor>, onImmersiveOrRestoreListenerCaptor: KArgumentCaptor<() -> Unit> = @@ -1376,6 +1427,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { ): DesktopModeWindowDecoration { val decor = setUpMockDecorationForTask(createTask( windowingMode = windowingMode, + displayId = displayId, requestingImmersive = requestingImmersive )) onTaskOpening(decor.mTaskInfo, taskSurface) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index 41f57ae0fd97..075349a51f74 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -64,6 +64,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.Region; import android.net.Uri; import android.os.Handler; import android.os.SystemProperties; @@ -224,6 +225,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private TestableContext mTestableContext; private final ShellExecutor mBgExecutor = new TestShellExecutor(); private final AssistContent mAssistContent = new AssistContent(); + private final Region mExclusionRegion = Region.obtain(); /** Set up run before test class. */ @BeforeClass @@ -283,7 +285,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); - spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */, mExclusionRegion); // Menus should close if open before the task being invisible causes relayout to return. verify(spyWindowDecor).closeHandleMenu(); @@ -303,7 +305,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL); } @@ -324,7 +327,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mCornerRadius).isGreaterThan(0); } @@ -350,7 +354,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(customTaskDensity); } @@ -377,12 +382,14 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(systemDensity); } @Test + @DisableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) public void updateRelayoutParams_freeformAndTransparentAppearance_allowsInputFallthrough() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -400,12 +407,39 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.hasInputFeatureSpy()).isTrue(); } @Test + @EnableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) + public void updateRelayoutParams_freeformAndTransparentAppearance_limitedTouchRegion() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance( + APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false, + /* isStatusBarVisible */ true, + /* isKeyguardVisibleAndOccluded */ false, + /* inFullImmersiveMode */ false, + new InsetsState(), + /* hasGlobalFocus= */ true, + mExclusionRegion); + + assertThat(relayoutParams.mLimitTouchRegionToSystemAreas).isTrue(); + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) public void updateRelayoutParams_freeformButOpaqueAppearance_disallowsInputFallthrough() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -422,12 +456,38 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.hasInputFeatureSpy()).isFalse(); } @Test + @EnableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) + public void updateRelayoutParams_freeformButOpaqueAppearance_unlimitedTouchRegion() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false, + /* isStatusBarVisible */ true, + /* isKeyguardVisibleAndOccluded */ false, + /* inFullImmersiveMode */ false, + new InsetsState(), + /* hasGlobalFocus= */ true, + mExclusionRegion); + + assertThat(relayoutParams.mLimitTouchRegionToSystemAreas).isFalse(); + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) public void updateRelayoutParams_fullscreen_disallowsInputFallthrough() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); @@ -443,11 +503,35 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.hasInputFeatureSpy()).isFalse(); } + @Test + @EnableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) + public void updateRelayoutParams_fullscreen_unlimitedTouchRegion() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false, + /* isStatusBarVisible */ true, + /* isKeyguardVisibleAndOccluded */ false, + /* inFullImmersiveMode */ false, + new InsetsState(), + /* hasGlobalFocus= */ true, + mExclusionRegion); + + assertThat(relayoutParams.mLimitTouchRegionToSystemAreas).isFalse(); + } + @Test public void updateRelayoutParams_freeform_inputChannelNeeded() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); @@ -464,7 +548,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(hasNoInputChannelFeature(relayoutParams)).isFalse(); } @@ -486,7 +571,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue(); } @@ -508,7 +594,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue(); } @@ -531,7 +618,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue(); } @@ -555,7 +643,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue(); } @@ -577,7 +666,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat( (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) @@ -601,7 +691,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat( (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) == 0) @@ -631,7 +722,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ true, insetsState, - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); // Takes status bar inset as padding, ignores caption bar inset. assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50); @@ -654,7 +746,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ true, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsInsetSource).isFalse(); } @@ -676,7 +769,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); // Header is always shown because it's assumed the status bar is always visible. assertThat(relayoutParams.mIsCaptionVisible).isTrue(); @@ -698,7 +792,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isTrue(); } @@ -719,7 +814,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isFalse(); } @@ -740,7 +836,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ true, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isFalse(); } @@ -762,7 +859,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ true, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isTrue(); @@ -776,7 +874,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ true, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isFalse(); } @@ -798,7 +897,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ true, /* inFullImmersiveMode */ true, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isFalse(); } @@ -809,7 +909,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockTransaction).apply(); verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any()); @@ -824,7 +924,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT) taskInfo.isResizeable = false; - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockTransaction, never()).apply(); verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction); @@ -836,7 +936,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any()); } @@ -848,7 +948,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); ArgumentCaptor runnableArgument = ArgumentCaptor.forClass(Runnable.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); // Once for view host, the other for the AppHandle input layer. verify(mMockHandler, times(2)).post(runnableArgument.capture()); @@ -865,7 +965,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT) taskInfo.isResizeable = false; - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any()); verify(mMockHandler, never()).post(any()); @@ -877,11 +977,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); ArgumentCaptor runnableArgument = ArgumentCaptor.forClass(Runnable.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); // Once for view host, the other for the AppHandle input layer. verify(mMockHandler, times(2)).post(runnableArgument.capture()); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockHandler).removeCallbacks(runnableArgument.getValue()); } @@ -892,7 +992,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); ArgumentCaptor runnableArgument = ArgumentCaptor.forClass(Runnable.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); // Once for view host, the other for the AppHandle input layer. verify(mMockHandler, times(2)).post(runnableArgument.capture()); @@ -1132,7 +1232,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { runnableArgument.getValue().run(); // Relayout decor with same captured link - decor.relayout(taskInfo, true /* hasGlobalFocus */); + decor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); // Verify handle menu's browser link not set to captured link since link is expired createHandleMenu(decor); @@ -1313,7 +1413,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockCaptionHandleRepository, never()).notifyCaptionChanged(any()); } @@ -1330,7 +1430,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged( captionStateArgumentCaptor.capture()); @@ -1357,7 +1457,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockAppHeaderViewHolder, atLeastOnce()).runOnAppChipGlobalLayout( runnableArgumentCaptor.capture()); runnableArgumentCaptor.getValue().invoke(); @@ -1380,7 +1480,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */, mExclusionRegion); verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged( captionStateArgumentCaptor.capture()); @@ -1400,7 +1500,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); createHandleMenu(spyWindowDecor); verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged( @@ -1425,7 +1525,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); createHandleMenu(spyWindowDecor); spyWindowDecor.closeHandleMenu(); @@ -1522,7 +1622,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { windowDecor.setOpenInBrowserClickListener(mMockOpenInBrowserClickListener); windowDecor.mDecorWindowContext = mContext; if (relayout) { - windowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + windowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); } return windowDecor; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 8e0434cb28f7..534803db5fe0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -60,6 +60,7 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.util.DisplayMetrics; @@ -508,7 +509,7 @@ public class WindowDecorationTests extends ShellTestCase { final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */, - true /* hasGlobalFocus */); + true /* hasGlobalFocus */, Region.obtain()); verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); } @@ -525,7 +526,7 @@ public class WindowDecorationTests extends ShellTestCase { mRelayoutParams.mCaptionTopPadding = 50; windowDecor.relayout(taskInfo, false /* applyStartTransactionOnDraw */, - true /* hasGlobalFocus */); + true /* hasGlobalFocus */, Region.obtain()); assertEquals(50, mRelayoutResult.mCaptionTopPadding); } @@ -944,7 +945,7 @@ public class WindowDecorationTests extends ShellTestCase { decor.onInsetsStateChanged(createInsetsState(statusBars(), false /* visible */)); - verify(decor, times(2)).relayout(task, true /* hasGlobalFocus */); + verify(decor, times(2)).relayout(any(), any(), any(), any(), any(), any()); } @Test @@ -958,7 +959,7 @@ public class WindowDecorationTests extends ShellTestCase { decor.onInsetsStateChanged(createInsetsState(statusBars(), true /* visible */)); - verify(decor, times(1)).relayout(task, true /* hasGlobalFocus */); + verify(decor, times(1)).relayout(any(), any(), any(), any(), any(), any()); } @Test @@ -973,7 +974,7 @@ public class WindowDecorationTests extends ShellTestCase { decor.onKeyguardStateChanged(true /* visible */, true /* occluding */); assertTrue(decor.mIsKeyguardVisibleAndOccluded); - verify(decor, times(2)).relayout(task, true /* hasGlobalFocus */); + verify(decor, times(2)).relayout(any(), any(), any(), any(), any(), any()); } @Test @@ -987,7 +988,7 @@ public class WindowDecorationTests extends ShellTestCase { decor.onKeyguardStateChanged(false /* visible */, true /* occluding */); - verify(decor, times(1)).relayout(task, true /* hasGlobalFocus */); + verify(decor, times(1)).relayout(any(), any(), any(), any(), any(), any()); } private ActivityManager.RunningTaskInfo createTaskInfo() { @@ -1061,9 +1062,16 @@ public class WindowDecorationTests extends ShellTestCase { surfaceControlViewHostFactory, desktopModeEventLogger); } - @Override void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) { - relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus); + relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus, + Region.obtain()); + } + + @Override + void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion) { + relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus, + displayExclusionRegion); } @Override @@ -1085,11 +1093,13 @@ public class WindowDecorationTests extends ShellTestCase { } void relayout(ActivityManager.RunningTaskInfo taskInfo, - boolean applyStartTransactionOnDraw, boolean hasGlobalFocus) { + boolean applyStartTransactionOnDraw, boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion) { mRelayoutParams.mRunningTaskInfo = taskInfo; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; mRelayoutParams.mLayoutResId = R.layout.caption_layout; mRelayoutParams.mHasGlobalFocus = hasGlobalFocus; + mRelayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion); relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT, mMockWindowContainerTransaction, mMockView, mRelayoutResult); } -- cgit v1.2.3-59-g8ed1b