diff options
7 files changed, 272 insertions, 118 deletions
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 a06b4a2e09d4..bfddaaa34132 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 @@ -128,6 +128,7 @@ import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener; +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer; import com.android.wm.shell.windowdecor.extension.InsetsStateKt; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; @@ -178,6 +179,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); + private DesktopStatusBarInputLayerSupplier mStatusBarInputLayerSupplier; private final ExclusionRegionListener mExclusionRegionListener = new ExclusionRegionListenerImpl(); @@ -411,7 +413,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, return Unit.INSTANCE; }); } - mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor); + if (Flags.enableHandleInputFix()) { + mStatusBarInputLayerSupplier = + new DesktopStatusBarInputLayerSupplier(mContext, mMainHandler); + mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor); + } } @Override @@ -467,6 +473,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, removeTaskFromEventReceiver(oldTaskInfo.displayId); incrementEventReceiverTasks(taskInfo.displayId); } + decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo)); decoration.relayout(taskInfo, decoration.mHasGlobalFocus); mActivityOrientationChangeHandler.ifPresent(handler -> handler.handleActivityOrientationChange(oldTaskInfo, taskInfo)); @@ -505,6 +512,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, if (decoration == null) { createWindowDecoration(taskInfo, taskSurface, startT, finishT); } else { + decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo)); decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */, mFocusTransitionObserver.hasGlobalFocus(taskInfo)); @@ -644,7 +652,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, decoration.closeHandleMenu(); // When the app enters split-select, the handle will no longer be visible, meaning // we shouldn't receive input for it any longer. - decoration.disposeStatusBarInputLayer(); + decoration.detachStatusBarInputLayer(); mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */); } @@ -1280,8 +1288,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, // should not be receiving any input. if (resultType == TO_SPLIT_LEFT_INDICATOR || resultType == TO_SPLIT_RIGHT_INDICATOR) { - relevantDecor.disposeStatusBarInputLayer(); - // We should also dispose the other split task's input layer if + relevantDecor.detachStatusBarInputLayer(); + // We should also detach the other split task's input layer if // applicable. final int splitPosition = mSplitScreenController .getSplitPosition(relevantDecor.mTaskInfo.taskId); @@ -1294,7 +1302,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mSplitScreenController.getTaskInfo(oppositePosition); if (oppositeTaskInfo != null) { mWindowDecorByTaskId.get(oppositeTaskInfo.taskId) - .disposeStatusBarInputLayer(); + .detachStatusBarInputLayer(); } } } @@ -1538,6 +1546,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, touchEventListener, touchEventListener, touchEventListener, touchEventListener); windowDecoration.setExclusionRegionListener(mExclusionRegionListener); windowDecoration.setDragPositioningCallback(taskPositioner); + windowDecoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo)); windowDecoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */, mFocusTransitionObserver.hasGlobalFocus(taskInfo)); @@ -1546,6 +1555,18 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } } + /** Decide which cached status bar input layer should be used for a decoration. */ + private AdditionalSystemViewContainer getStatusBarInputLayer( + RunningTaskInfo taskInfo + ) { + if (mStatusBarInputLayerSupplier == null) return null; + return mStatusBarInputLayerSupplier.getStatusBarInputLayer( + taskInfo, + mSplitScreenController.getSplitPosition(taskInfo.taskId), + mSplitScreenController.isLeftRightSplit() + ); + } + private RunningTaskInfo getOtherSplitTask(int taskId) { @SplitPosition int remainingTaskPosition = mSplitScreenController .getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT 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 6eb20b9e3ae5..2b08ba0d7ab2 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 @@ -101,6 +101,7 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder; import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder; @@ -197,6 +198,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private final MultiInstanceHelper mMultiInstanceHelper; private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository; private final DesktopRepository mDesktopRepository; + private AdditionalSystemViewContainer mStatusBarInputLayer; DesktopModeWindowDecoration( Context context, @@ -494,13 +496,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin notifyNoCaptionHandle(); } mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId); - disposeStatusBarInputLayer(); + detachStatusBarInputLayer(); Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces return; } if (oldRootView != mResult.mRootView) { - disposeStatusBarInputLayer(); + detachStatusBarInputLayer(); mWindowDecorViewHolder = createViewHolder(); } Trace.beginSection("DesktopModeWindowDecoration#relayout-binding"); @@ -518,6 +520,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight, isCaptionVisible() )); + if (mStatusBarInputLayer != null) { + asAppHandle(mWindowDecorViewHolder).bindStatusBarInputLayer(mStatusBarInputLayer); + } } else { mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData( mTaskInfo, @@ -727,15 +732,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } /** - * Dispose of the view used to forward inputs in status bar region. Intended to be + * Detach the status bar input layer from this decoration. Intended to be * used any time handle is no longer visible. */ - void disposeStatusBarInputLayer() { + void detachStatusBarInputLayer() { if (!isAppHandle(mWindowDecorViewHolder) || !Flags.enableHandleInputFix()) { return; } - asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer(); + asAppHandle(mWindowDecorViewHolder).detachStatusBarInputLayer(); } private WindowDecorationViewHolder createViewHolder() { @@ -1563,7 +1568,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin closeManageWindowsMenu(); mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId); disposeResizeVeil(); - disposeStatusBarInputLayer(); + detachStatusBarInputLayer(); clearCurrentViewHostRunnable(); if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) { notifyNoCaptionHandle(); @@ -1680,6 +1685,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin + "}"; } + /** + * Set the view container to be used to forward input through status bar. Null in cases + * where input forwarding isn't needed. + */ + public void setStatusBarInputLayer( + @Nullable AdditionalSystemViewContainer additionalSystemViewContainer + ) { + mStatusBarInputLayer = additionalSystemViewContainer; + } + static class Factory { DesktopModeWindowDecoration create( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt new file mode 100644 index 000000000000..025bb403ec8b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt @@ -0,0 +1,117 @@ +/* + * 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 + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration +import android.content.Context +import android.graphics.PixelFormat +import android.os.Handler +import android.view.Gravity +import android.view.View +import android.view.WindowManager +import com.android.wm.shell.shared.annotations.ShellMainThread +import com.android.wm.shell.shared.split.SplitScreenConstants +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer + +/** + * Supplier for [AdditionalSystemViewContainer] objects to be used for forwarding input + * events through status bar to an app handle. Currently supports two simultaneous input layers. + * + * The supplier will pick one of two input layer view containers to use: one for tasks in + * fullscreen or top/left split stage, and one for tasks in right split stage. + */ +class DesktopStatusBarInputLayerSupplier( + private val context: Context, + @ShellMainThread handler: Handler +) { + private val inputLayers: MutableList<AdditionalSystemViewContainer> = mutableListOf() + + init { + // Post this as creation of the input layer views is a relatively expensive operation. + handler.post { + repeat(TOTAL_INPUT_LAYERS) { + inputLayers.add(createInputLayer()) + } + } + } + + private fun createInputLayer(): AdditionalSystemViewContainer { + val lp = WindowManager.LayoutParams( + WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSPARENT + ) + lp.title = "Desktop status bar input layer" + lp.gravity = Gravity.LEFT or Gravity.TOP + lp.setTrustedOverlay() + + // Make this window a spy window to enable it to pilfer pointers from the system-wide + // gesture listener that receives events before window. This is to prevent notification + // shade gesture when we swipe down to enter desktop. + lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY + lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + val view = View(context) + view.visibility = View.INVISIBLE + return AdditionalSystemViewContainer( + WindowManagerWrapper( + context.getSystemService<WindowManager>(WindowManager::class.java) + ), + view, + lp + ) + } + + /** + * Decide which cached status bar input layer should be used for a decoration, if any. + * + * [splitPosition] and [isLeftRightSplit] are used to determine which input layer we use. + * The first one is reserved for fullscreen tasks or tasks in top/left split, + * while the second one is exclusively used for tasks in right split stage. Note we care about + * left-right vs top-bottom split as the bottom stage should not use an input layer. + */ + fun getStatusBarInputLayer( + taskInfo: RunningTaskInfo, + @SplitScreenConstants.SplitPosition splitPosition: Int, + isLeftRightSplit: Boolean + ): AdditionalSystemViewContainer? { + if (!taskInfo.isVisibleRequested) return null + // Fullscreen and top/left split tasks will use the first input layer. + if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN + || splitPosition == SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT + ) { + return inputLayers[LEFT_TOP_INPUT_LAYER] + } + // Right split tasks will use the second one. + if (isLeftRightSplit && splitPosition == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT + ) { + return inputLayers[RIGHT_SPLIT_INPUT_LAYER] + } + // Which leaves bottom split and freeform tasks, which do not need an input layer + // as the status bar is not blocking them. + return null + } + + companion object { + private const val TOTAL_INPUT_LAYERS = 2 + // Input layer index for fullscreen tasks and tasks in top-left split + private const val LEFT_TOP_INPUT_LAYER = 0 + // Input layer index for tasks in right split stage. Does not include bottom split as that + // stage is not blocked by status bar. + private const val RIGHT_SPLIT_INPUT_LAYER = 1 + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt index 8b6aaaf619e0..1451f363ec73 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt @@ -23,8 +23,8 @@ import android.view.Gravity import android.view.LayoutInflater import android.view.SurfaceControl import android.view.View -import android.view.WindowInsets import android.view.WindowManager +import android.view.WindowManager.LayoutParams import com.android.wm.shell.windowdecor.WindowManagerWrapper /** @@ -33,27 +33,11 @@ import com.android.wm.shell.windowdecor.WindowManagerWrapper */ class AdditionalSystemViewContainer( private val windowManagerWrapper: WindowManagerWrapper, - taskId: Int, - x: Int, - y: Int, - width: Int, - height: Int, - flags: Int, - @WindowInsets.Type.InsetsType forciblyShownTypes: Int = 0, - override val view: View + override val view: View, + val lp: LayoutParams ) : AdditionalViewContainer() { - val lp: WindowManager.LayoutParams = WindowManager.LayoutParams( - width, height, x, y, - WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL, - flags, - PixelFormat.TRANSPARENT - ).apply { - title = "Additional view container of Task=$taskId" - gravity = Gravity.LEFT or Gravity.TOP - setTrustedOverlay() - this.forciblyShownTypes = forciblyShownTypes - } + /** Provide a layout id of a view to inflate for this view container. */ constructor( context: Context, windowManagerWrapper: WindowManagerWrapper, @@ -66,15 +50,30 @@ class AdditionalSystemViewContainer( @LayoutRes layoutId: Int ) : this( windowManagerWrapper = windowManagerWrapper, - taskId = taskId, - x = x, - y = y, - width = width, - height = height, - flags = flags, - view = LayoutInflater.from(context).inflate(layoutId, null /* parent */) + view = LayoutInflater.from(context).inflate(layoutId, null /* parent */), + lp = createLayoutParams(x, y, width, height, flags, taskId) ) + /** Provide a view directly for this view container */ + constructor( + windowManagerWrapper: WindowManagerWrapper, + taskId: Int, + x: Int, + y: Int, + width: Int, + height: Int, + flags: Int, + view: View, + forciblyShownTypes: Int = 0 + ) : this( + windowManagerWrapper = windowManagerWrapper, + view = view, + lp = createLayoutParams(x, y, width, height, flags, taskId).apply { + this.forciblyShownTypes = forciblyShownTypes + } + ) + + /** Do not supply a view at all, instead creating the view container with a basic view. */ constructor( context: Context, windowManagerWrapper: WindowManagerWrapper, @@ -86,12 +85,7 @@ class AdditionalSystemViewContainer( flags: Int ) : this( windowManagerWrapper = windowManagerWrapper, - taskId = taskId, - x = x, - y = y, - width = width, - height = height, - flags = flags, + lp = createLayoutParams(x, y, width, height, flags, taskId), view = View(context) ) @@ -104,7 +98,7 @@ class AdditionalSystemViewContainer( } override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) { - val lp = (view.layoutParams as WindowManager.LayoutParams).apply { + lp.apply { this.x = x.toInt() this.y = y.toInt() } @@ -124,13 +118,29 @@ class AdditionalSystemViewContainer( ): AdditionalSystemViewContainer = AdditionalSystemViewContainer( windowManagerWrapper = windowManagerWrapper, - taskId = taskId, - x = x, - y = y, - width = width, - height = height, - flags = flags, - view = view + view = view, + lp = createLayoutParams(x, y, width, height, flags, taskId) ) } + companion object { + fun createLayoutParams( + x: Int, + y: Int, + width: Int, + height: Int, + flags: Int, + taskId: Int + ): LayoutParams { + return LayoutParams( + width, height, x, y, + LayoutParams.TYPE_STATUS_BAR_ADDITIONAL, + flags, + PixelFormat.TRANSPARENT + ).apply { + title = "Additional view container of Task=$taskId" + gravity = Gravity.LEFT or Gravity.TOP + setTrustedOverlay() + } + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt index b5700ffb046b..b43a9839f042 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt @@ -36,13 +36,10 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction import android.widget.ImageButton import androidx.core.view.ViewCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat -import com.android.internal.policy.SystemBarUtils -import com.android.window.flags.Flags import com.android.wm.shell.R import com.android.wm.shell.shared.animation.Interpolators import com.android.wm.shell.windowdecor.WindowManagerWrapper import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer -import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder.Data /** * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split). @@ -69,10 +66,12 @@ internal class AppHandleViewHolder( ) : Data() private lateinit var taskInfo: RunningTaskInfo + private val position: Point = Point() + private var width: Int = 0 + private var height: Int = 0 private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption) private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle) private val inputManager = context.getSystemService(InputManager::class.java) - private var statusBarInputLayerExists = false // An invisible View that takes up the same coordinates as captionHandle but is layered // above the status bar. The purpose of this View is to receive input intended for @@ -112,21 +111,54 @@ internal class AppHandleViewHolder( ) { captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo)) this.taskInfo = taskInfo - // If handle is not in status bar region(i.e., bottom stage in vertical split), - // do not create an input layer - if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return - if (!isCaptionVisible && statusBarInputLayerExists) { - disposeStatusBarInputLayer() + this.position.set(position) + this.width = width + this.height = height + if (!isCaptionVisible && statusBarInputLayer != null) { + detachStatusBarInputLayer() return } - // Input layer view creation / modification takes a significant amount of time; + } + + fun bindStatusBarInputLayer( + statusBarLayer: AdditionalSystemViewContainer + ) { + // Input layer view modification takes a significant amount of time; // post them so we don't hold up DesktopModeWindowDecoration#relayout. - if (statusBarInputLayerExists) { + if (statusBarLayer == statusBarInputLayer) { handler.post { updateStatusBarInputLayer(position) } - } else { - // Input layer is created on a delay; prevent multiple from being created. - statusBarInputLayerExists = true - handler.post { createStatusBarInputLayer(position, width, height) } + return + } + // Remove the old input layer when changing to a new one. + if (statusBarInputLayer != null) detachStatusBarInputLayer() + if (statusBarLayer.view.visibility == View.INVISIBLE) { + statusBarLayer.view.visibility = View.VISIBLE + } + statusBarInputLayer = statusBarLayer + statusBarInputLayer?.let { + inputLayer -> setupAppHandleA11y(inputLayer.view) + } + handler.post { + val view = statusBarInputLayer?.view + ?: error("Unable to find statusBarInputLayer View") + // Caption handle is located within the status bar region, meaning the + // DisplayPolicy will attempt to transfer this input to status bar if it's + // a swipe down. Pilfer here to keep the gesture in handle alone. + view.setOnTouchListener { v, event -> + if (event.actionMasked == ACTION_DOWN) { + inputManager.pilferPointers(v.viewRootImpl.inputToken) + } + captionHandle.dispatchTouchEvent(event) + return@setOnTouchListener true + } + view.setOnHoverListener { _, event -> + captionHandle.onHoverEvent(event) + } + val lp = statusBarInputLayer?.view?.layoutParams as WindowManager.LayoutParams + lp.x = position.x + lp.y = position.y + lp.width = width + lp.height = height } } @@ -138,40 +170,6 @@ internal class AppHandleViewHolder( animateCaptionHandleAlpha(startValue = 0f, endValue = 1f) } - private fun createStatusBarInputLayer(handlePosition: Point, - handleWidth: Int, - handleHeight: Int) { - if (!Flags.enableHandleInputFix()) return - statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper, - taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - ) - val view = statusBarInputLayer?.view ?: error("Unable to find statusBarInputLayer View") - val lp = statusBarInputLayer?.lp ?: error("Unable to find statusBarInputLayer " + - "LayoutParams") - lp.title = "Handle Input Layer of task " + taskInfo.taskId - lp.setTrustedOverlay() - // Make this window a spy window to enable it to pilfer pointers from the system-wide - // gesture listener that receives events before window. This is to prevent notification - // shade gesture when we swipe down to enter desktop. - lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY - view.setOnHoverListener { _, event -> - captionHandle.onHoverEvent(event) - } - // Caption handle is located within the status bar region, meaning the - // DisplayPolicy will attempt to transfer this input to status bar if it's - // a swipe down. Pilfer here to keep the gesture in handle alone. - view.setOnTouchListener { v, event -> - if (event.actionMasked == ACTION_DOWN) { - inputManager.pilferPointers(v.viewRootImpl.inputToken) - } - captionHandle.dispatchTouchEvent(event) - return@setOnTouchListener true - } - setupAppHandleA11y(view) - windowManagerWrapper.updateViewLayout(view, lp) - } - private fun setupAppHandleA11y(view: View) { view.accessibilityDelegate = object : View.AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo( @@ -224,15 +222,12 @@ internal class AppHandleViewHolder( } /** - * Remove the input layer from [WindowManager]. Should be used when caption handle - * is not visible. + * Remove the input listeners from the input layer and remove it from this view holder. */ - fun disposeStatusBarInputLayer() { - statusBarInputLayerExists = false - handler.post { - statusBarInputLayer?.releaseView() - statusBarInputLayer = null - } + fun detachStatusBarInputLayer() { + statusBarInputLayer?.view?.setOnTouchListener(null) + statusBarInputLayer?.view?.setOnHoverListener(null) + statusBarInputLayer = null } private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int { 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 175fbd2396e3..470974e80c6b 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 @@ -909,7 +909,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { } @Test - fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() { + fun testDecor_onClickToSplitScreen_detachesStatusBarInputLayer() { val toSplitScreenListenerCaptor = forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>> val decor = createOpenTaskDecoration( @@ -919,7 +919,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { toSplitScreenListenerCaptor.value.invoke() - verify(decor).disposeStatusBarInputLayer() + verify(decor).detachStatusBarInputLayer() } @Test 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 320887212f54..3ec6eaaf0d91 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 @@ -49,7 +49,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.kotlin.VerificationKt.times; import android.app.ActivityManager; import android.app.assist.AssistContent; @@ -847,8 +846,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); - // Once for view host, the other for the AppHandle input layer. - verify(mMockHandler, times(2)).post(runnableArgument.capture()); + verify(mMockHandler).post(runnableArgument.capture()); runnableArgument.getValue().run(); verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any()); } @@ -875,8 +873,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); - // Once for view host, the other for the AppHandle input layer. - verify(mMockHandler, times(2)).post(runnableArgument.capture()); + verify(mMockHandler).post(runnableArgument.capture()); spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); @@ -890,8 +887,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); - // Once for view host, the other for the AppHandle input layer. - verify(mMockHandler, times(2)).post(runnableArgument.capture()); + verify(mMockHandler).post(runnableArgument.capture()); spyWindowDecor.close(); |