diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index f757e1c88cb82f58c4d848e5057a57d6136cba3e..fb3c35b6a1e31eb45bf8f71ce39f54b100807a16 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -201,9 +201,11 @@ public abstract class WMShellModule { @Provides static WindowDecorViewModel provideWindowDecorViewModel( Context context, + @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler, @ShellMainThread Choreographer mainChoreographer, ShellInit shellInit, + IWindowManager windowManager, ShellCommandHandler shellCommandHandler, ShellTaskOrganizer taskOrganizer, DisplayController displayController, @@ -216,10 +218,12 @@ public abstract class WMShellModule { if (DesktopModeStatus.isEnabled()) { return new DesktopModeWindowDecorViewModel( context, + mainExecutor, mainHandler, mainChoreographer, shellInit, shellCommandHandler, + windowManager, taskOrganizer, displayController, shellController, 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 96eaa1edbae477b8e201ecc385ab6a57feac3fac..91e9601c6a2768d1d16d8c5233f9f80b189106e4 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 @@ -190,6 +190,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL mRelayoutParams.mShadowRadiusId = shadowRadiusID; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; mRelayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition; + mRelayoutParams.mAllowCaptionInputFallthrough = false; relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index caa894fcbbc735a3f76cb6a5ec8dcd81e496908c..8d798a3035f636da6d2325903584ef3e627e5159 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -48,9 +48,13 @@ import android.graphics.Region; import android.hardware.input.InputManager; import android.os.Handler; import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; import android.util.SparseArray; import android.view.Choreographer; import android.view.GestureDetector; +import android.view.ISystemGestureExclusionListener; +import android.view.IWindowManager; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventReceiver; @@ -73,6 +77,7 @@ import com.android.wm.shell.ShellTaskOrganizer; 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.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.desktopmode.DesktopModeStatus; @@ -88,6 +93,7 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener; +import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import java.io.PrintWriter; import java.util.Optional; @@ -102,6 +108,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private static final String TAG = "DesktopModeWindowDecorViewModel"; private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory; + private final IWindowManager mWindowManager; + private final ShellExecutor mMainExecutor; private final ActivityTaskManager mActivityTaskManager; private final ShellCommandHandler mShellCommandHandler; private final ShellTaskOrganizer mTaskOrganizer; @@ -112,6 +120,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; private final Optional<DesktopTasksController> mDesktopTasksController; + private final InputManager mInputManager; + private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); @@ -135,14 +145,31 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { new DesktopModeKeyguardChangeListener(); private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; private final DisplayInsetsController mDisplayInsetsController; + private final Region mExclusionRegion = Region.obtain(); private boolean mInImmersiveMode; + private final ISystemGestureExclusionListener mGestureExclusionListener = + new ISystemGestureExclusionListener.Stub() { + @Override + public void onSystemGestureExclusionChanged(int displayId, + Region systemGestureExclusion, Region systemGestureExclusionUnrestricted) { + if (mContext.getDisplayId() != displayId) { + return; + } + mMainExecutor.execute(() -> { + mExclusionRegion.set(systemGestureExclusion); + }); + } + }; + public DesktopModeWindowDecorViewModel( Context context, + ShellExecutor shellExecutor, Handler mainHandler, Choreographer mainChoreographer, ShellInit shellInit, ShellCommandHandler shellCommandHandler, + IWindowManager windowManager, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, @@ -154,10 +181,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { ) { this( context, + shellExecutor, mainHandler, mainChoreographer, shellInit, shellCommandHandler, + windowManager, taskOrganizer, displayController, shellController, @@ -174,10 +203,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @VisibleForTesting DesktopModeWindowDecorViewModel( Context context, + ShellExecutor shellExecutor, Handler mainHandler, Choreographer mainChoreographer, ShellInit shellInit, ShellCommandHandler shellCommandHandler, + IWindowManager windowManager, ShellTaskOrganizer taskOrganizer, DisplayController displayController, ShellController shellController, @@ -190,6 +221,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { Supplier<SurfaceControl.Transaction> transactionFactory, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { mContext = context; + mMainExecutor = shellExecutor; mMainHandler = mainHandler; mMainChoreographer = mainChoreographer; mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class); @@ -201,10 +233,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mTransitions = transitions; mDesktopTasksController = desktopTasksController; mShellCommandHandler = shellCommandHandler; + mWindowManager = windowManager; mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; mTransactionFactory = transactionFactory; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; + mInputManager = mContext.getSystemService(InputManager.class); shellInit.addInitCallback(this::onInit, this); } @@ -216,6 +250,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { new DesktopModeOnInsetsChangedListener()); mDesktopTasksController.ifPresent(c -> c.setOnTaskResizeAnimationListener( new DeskopModeOnTaskResizeAnimationListener())); + try { + mWindowManager.registerSystemGestureExclusionListener(mGestureExclusionListener, + mContext.getDisplayId()); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register window manager callbacks", e); + } } @Override @@ -315,12 +355,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener, View.OnGenericMotionListener , DragDetector.MotionEventHandler { private static final int CLOSE_MAXIMIZE_MENU_DELAY_MS = 150; + private final int mTaskId; private final WindowContainerToken mTaskToken; private final DragPositioningCallback mDragPositioningCallback; private final DragDetector mDragDetector; private final GestureDetector mGestureDetector; + /** + * 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 + * instead of by the app for their custom header content. + */ + private boolean mShouldPilferCaptionEvents; private boolean mIsDragging; private boolean mTouchscreenInUse; private boolean mHasLongClicked; @@ -438,6 +485,40 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); moveTaskToFront(decoration.mTaskInfo); + final int actionMasked = e.getActionMasked(); + final boolean isDown = actionMasked == MotionEvent.ACTION_DOWN; + final boolean isUpOrCancel = actionMasked == MotionEvent.ACTION_CANCEL + || actionMasked == MotionEvent.ACTION_UP; + if (isDown) { + final boolean downInCustomizableCaptionRegion = + decoration.checkTouchEventInCustomizableRegion(e); + final boolean downInExclusionRegion = mExclusionRegion.contains( + (int) e.getRawX(), (int) e.getRawY()); + final boolean isTransparentCaption = + TaskInfoKt.isTransparentCaptionBarAppearance(decoration.mTaskInfo); + // 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 + // sure to cancel these events if they do not happen in the intersection of the + // 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); + } + + if (!mShouldPilferCaptionEvents) { + // The event will be handled by a window below. + return false; + } + // Otherwise pilfer so that windows below receive cancellations for this gesture, and + // continue normal handling as a caption gesture. + if (mInputManager != null) { + mInputManager.pilferPointers(v.getViewRootImpl().getInputToken()); + } + if (isUpOrCancel) { + // Gesture is finished, reset state. + mShouldPilferCaptionEvents = false; + } if (!mHasLongClicked && id != R.id.maximize_window) { decoration.closeMaximizeMenuIfNeeded(e); } 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 74f460bf12263d2b568204d83c3dfec4d8639040..9e999ae52835b82a4fe90bec8346fa2522b254a1 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 @@ -56,6 +56,7 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; +import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder; import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder; import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder; @@ -337,6 +338,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin controlsElement.mWidthResId = R.dimen.desktop_mode_right_edge_buttons_width; controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END; relayoutParams.mOccludingCaptionElements.add(controlsElement); + relayoutParams.mAllowCaptionInputFallthrough = + TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo); } if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ taskInfo.isFocused)) { relayoutParams.mShadowRadiusId = taskInfo.isFocused @@ -698,6 +701,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin && inputPoint.y <= mResult.mCaptionHeight; } + /** + * Checks whether the touch event falls inside the customizable caption region. + */ + boolean checkTouchEventInCustomizableRegion(MotionEvent ev) { + return mResult.mCustomizableCaptionRegion.contains((int) ev.getRawX(), (int) ev.getRawY()); + } + /** * Check a passed MotionEvent if a click has occurred on any button on this caption * Note this should only be called when a regular onClick is not possible diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index dc65855646eaa4c42d58152caf7c94a5a4876b26..32c2d1e9b257b2682b2bf65bb9d28b2f52188f21 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -31,6 +31,7 @@ import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.os.Binder; import android.view.Display; import android.view.InsetsSource; @@ -311,6 +312,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> if (numOfElements == 0) { boundingRects = null; } else { + // The customizable region can at most be equal to the caption bar. + if (params.mAllowCaptionInputFallthrough) { + outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect); + } boundingRects = new Rect[numOfElements]; for (int i = 0; i < numOfElements; i++) { final OccludingCaptionElement element = @@ -319,9 +324,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> resources.getDimensionPixelSize(element.mWidthResId); boundingRects[i] = calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect); + // Subtract the regions used by the caption elements, the rest is + // customizable. + if (params.mAllowCaptionInputFallthrough) { + outResult.mCustomizableCaptionRegion.op(boundingRects[i], + Region.Op.DIFFERENCE); + } } } - // Add this caption as an inset source. wct.addInsetsSource(mTaskInfo.token, mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect, @@ -389,6 +399,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); lp.setTitle("Caption of Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); + if (params.mAllowCaptionInputFallthrough) { + lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY; + } else { + lp.inputFeatures &= ~WindowManager.LayoutParams.INPUT_FEATURE_SPY; + } if (mViewHost == null) { mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay, mCaptionWindowManager); @@ -596,6 +611,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mCaptionHeightId; int mCaptionWidthId; final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>(); + boolean mAllowCaptionInputFallthrough; int mShadowRadiusId; int mCornerRadius; @@ -610,6 +626,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionHeightId = Resources.ID_NULL; mCaptionWidthId = Resources.ID_NULL; mOccludingCaptionElements.clear(); + mAllowCaptionInputFallthrough = false; mShadowRadiusId = Resources.ID_NULL; mCornerRadius = 0; @@ -637,6 +654,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mCaptionHeight; int mCaptionWidth; int mCaptionX; + final Region mCustomizableCaptionRegion = Region.obtain(); int mWidth; int mHeight; T mRootView; @@ -647,6 +665,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionHeight = 0; mCaptionWidth = 0; mCaptionX = 0; + mCustomizableCaptionRegion.setEmpty(); mRootView = null; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt new file mode 100644 index 0000000000000000000000000000000000000000..5dd96aceaec7c0193b528c92c6bd972e51211154 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 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.wm.shell.windowdecor.extension + +import android.app.TaskInfo +import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS +import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND + +val TaskInfo.isTransparentCaptionBarAppearance: Boolean + get() { + val appearance = taskDescription?.statusBarAppearance ?: 0 + return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0 + } + +val TaskInfo.isLightCaptionBarAppearance: Boolean + get() { + val appearance = taskDescription?.statusBarAppearance ?: 0 + return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0 + } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt index 7e5b9bd649f20309ce555a120acd9931ad29674c..b7dd01faa5430699ffd5eca7e4eafeb0254896d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt @@ -8,8 +8,6 @@ import android.graphics.Bitmap import android.graphics.Color import android.view.View import android.view.View.OnLongClickListener -import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS -import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView @@ -22,6 +20,8 @@ import com.android.internal.R.attr.materialColorSurfaceContainerLow import com.android.internal.R.attr.materialColorSurfaceDim import com.android.wm.shell.R import com.android.wm.shell.windowdecor.MaximizeButtonView +import com.android.wm.shell.windowdecor.extension.isLightCaptionBarAppearance +import com.android.wm.shell.windowdecor.extension.isTransparentCaptionBarAppearance /** * A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts @@ -107,7 +107,7 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( @ColorInt private fun getCaptionBackgroundColor(taskInfo: RunningTaskInfo): Int { - if (isTransparentBackgroundRequested(taskInfo)) { + if (taskInfo.isTransparentCaptionBarAppearance) { return Color.TRANSPARENT } val materialColorAttr: Int = @@ -133,10 +133,10 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( @ColorInt private fun getAppNameAndButtonColor(taskInfo: RunningTaskInfo): Int { val materialColorAttr = when { - isTransparentBackgroundRequested(taskInfo) && - isLightCaptionBar(taskInfo) -> materialColorOnSecondaryContainer - isTransparentBackgroundRequested(taskInfo) && - !isLightCaptionBar(taskInfo) -> materialColorOnSurface + taskInfo.isTransparentCaptionBarAppearance && + taskInfo.isLightCaptionBarAppearance -> materialColorOnSecondaryContainer + taskInfo.isTransparentCaptionBarAppearance && + !taskInfo.isLightCaptionBarAppearance -> materialColorOnSurface isDarkMode() -> materialColorOnSurface else -> materialColorOnSecondaryContainer } @@ -167,16 +167,6 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder( Configuration.UI_MODE_NIGHT_YES } - private fun isTransparentBackgroundRequested(taskInfo: RunningTaskInfo): Boolean { - val appearance = taskInfo.taskDescription?.statusBarAppearance ?: 0 - return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0 - } - - private fun isLightCaptionBar(taskInfo: RunningTaskInfo): Boolean { - val appearance = taskInfo.taskDescription?.statusBarAppearance ?: 0 - return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0 - } - companion object { private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder" private const val DARK_THEME_UNFOCUSED_OPACITY = 140 // 55% 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 f84685a92b57fa639d91088284cc5f04dc04c95d..917fd715f71ff2f29191ababc72a15cc17e2ee93 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 @@ -31,6 +31,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.Choreographer import android.view.Display.DEFAULT_DISPLAY +import android.view.IWindowManager import android.view.InputChannel import android.view.InputMonitor import android.view.InsetsSource @@ -96,6 +97,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockShellExecutor: ShellExecutor @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler + @Mock private lateinit var mockWindowManager: IWindowManager private val transactionFactory = Supplier<SurfaceControl.Transaction> { SurfaceControl.Transaction() @@ -110,10 +112,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { shellInit = ShellInit(mockShellExecutor) desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( mContext, + mockShellExecutor, mockMainHandler, mockMainChoreographer, shellInit, mockShellCommandHandler, + mockWindowManager, mockTaskOrganizer, mockDisplayController, mockShellController, 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 40e61dd95f51bb382d39694607f1ad462bb30a2d..9e62bd254ac548f0289fd5df0be2ec4e67208ad4 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 @@ -16,6 +16,10 @@ package com.android.wm.shell.windowdecor; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; @@ -168,6 +172,57 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { assertThat(relayoutParams.mCornerRadius).isGreaterThan(0); } + @Test + public void updateRelayoutParams_freeformAndTransparent_allowsInputFallthrough() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setStatusBarAppearance( + APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat(relayoutParams.mAllowCaptionInputFallthrough).isTrue(); + } + + @Test + public void updateRelayoutParams_freeformButOpaque_disallowsInputFallthrough() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setStatusBarAppearance(0); + final RelayoutParams relayoutParams = new RelayoutParams(); + + DesktopModeWindowDecoration.updateRelayoutParams( + relayoutParams, + mTestableContext, + taskInfo, + /* applyStartTransactionOnDraw= */ true, + /* shouldSetTaskPositionAndCrop */ false); + + assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse(); + } + + @Test + public void updateRelayoutParams_fullscreen_disallowsInputFallthrough() { + 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); + + assertThat(relayoutParams.mAllowCaptionInputFallthrough).isFalse(); + } + private void fillRoundedCornersResources(int fillValue) { when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt())) .thenReturn(fillValue);