diff options
| author | 2025-02-10 23:11:57 -0800 | |
|---|---|---|
| committer | 2025-02-14 11:17:33 -0800 | |
| commit | 727a2180ed0f4ea477fda5a6c731eb4d27b85db8 (patch) | |
| tree | 0e63f33d44313733be195ff70a7268f7c181e6ba | |
| parent | 9cf8d0f74b651caa45cf0b21180c9eb517d7e185 (diff) | |
Fix various issues with offscreen touch zones
1) When touch zones were released, the ViewHost was being released, but not the root leash. Now the root leash is removed as well.
2) The swap animation was calculating the wrong end position for the divider, because it measured from the screen edge instead of accounting for the offscreen app's offset.
3) The touch layer was being animated while still active, meaning that if the surface passed under the user's finger, it would register a touch. Now the touch layer is deactivated and removed prior to animation.
Fixes: 391868062
Fixes: 391866253
Flag: com.android.wm.shell.enable_flexible_two_app_split
Test: App surfaces no longer jump cut when swapping on the right side, and winscope is clear of any extra leashes.
Change-Id: I37103658c9082cfa02a870cb8ab30d0baae7c04a
3 files changed, 29 insertions, 3 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OffscreenTouchZone.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OffscreenTouchZone.java index 381f0b037023..3211307c6f9b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OffscreenTouchZone.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OffscreenTouchZone.java @@ -56,6 +56,7 @@ public class OffscreenTouchZone { /** The function that will be run when this zone is tapped. */ private final Runnable mOnClickRunnable; private SurfaceControlViewHost mViewHost; + private SurfaceControl mLeash; /** * @param isTopLeft Whether the desired touch zone will be on the top/left or the bottom/right @@ -96,6 +97,7 @@ public class OffscreenTouchZone { .setCallsite("OffscreenTouchZone::init"); builder.setParent(stageRoot); SurfaceControl leash = builder.build(); + mLeash = leash; // Create a ViewHost that will hold our view. WindowlessWindowManager wwm = new WindowlessWindowManager(config, leash, null); @@ -117,10 +119,14 @@ public class OffscreenTouchZone { } /** Releases the touch zone when it's no longer needed. */ - void release() { + void release(SurfaceControl.Transaction t) { if (mViewHost != null) { mViewHost.release(); } + if (mLeash != null) { + t.remove(mLeash); + mLeash = null; + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index cd5c135691d7..30ed713fa09d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -451,7 +451,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return; } - mOffscreenTouchZones.forEach(OffscreenTouchZone::release); + // TODO (b/349828130): It would be good to reuse a Transaction from StageCoordinator's + // mTransactionPool here, but passing it through SplitLayout and specifically + // SplitLayout.release() is complicated because that function is purposely called with a + // null value sometimes. When that function is refactored, we should also pass the + // Transaction in here. + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mOffscreenTouchZones.forEach(touchZone -> touchZone.release(t)); + t.apply(); mOffscreenTouchZones.clear(); } @@ -965,8 +972,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange final boolean shouldVeil = insets.left != 0 || insets.top != 0 || insets.right != 0 || insets.bottom != 0; + // Find the "left/top"-most position of the app surface -- usually 0, but sometimes negative + // if the left/top app is offscreen. + int leftTop = 0; + if (Flags.enableFlexibleTwoAppSplit()) { + leftTop = mIsLeftRightSplit ? getTopLeftBounds().left : getTopLeftBounds().top; + } + final int dividerPos = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget( - mIsLeftRightSplit ? getBottomRightBounds().width() : getBottomRightBounds().height() + leftTop + (mIsLeftRightSplit + ? getBottomRightBounds().width() : getBottomRightBounds().height()) ).position; final Rect endBounds1 = new Rect(); final Rect endBounds2 = new Rect(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 511e426cc681..cb005af236da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -1295,6 +1295,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, WindowContainerTransaction noFocus = new WindowContainerTransaction(); noFocus.setFocusable(mRootTaskInfo.token, false); mSyncQueue.queue(noFocus); + // Remove touch layers, since offscreen apps coming onscreen will not need their touch + // layers anymore. populateTouchZones() is called in the end callback to inflate new touch + // layers in the appropriate places. + mSplitLayout.removeTouchZones(); mSplitLayout.playSwapAnimation(t, topLeftStage, bottomRightStage, insets -> { @@ -1315,6 +1319,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSyncQueue.runInSync(st -> { mSplitLayout.updateStateWithCurrentPosition(); updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */); + mSplitLayout.populateTouchZones(); // updateSurfaceBounds(), above, officially puts the two apps in their new // stages. Starting on the next frame, all calculations are made using the |