summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt92
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt109
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java10
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();