From da44526b9b625da8beb4ac754fc004cbb37ea477 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Thu, 15 Jul 2021 02:52:15 +0000 Subject: [PATCH] Fix the content of the bottom split being truncated while showing IME Freeze the configuration size when shifting split layout so that client apps can calculate insets properly and won't get config or relaunch. Fix: 192410210 Test: atest WMShellUnitTests Test: verified the content of the bottom split won't be truncated when adjusting split layout with IME and won't get relaunch. Change-Id: I38da063ffacc19b691c14b74d118949293414492 --- .../android/wm/shell/apppairs/AppPair.java | 13 +++- .../wm/shell/common/split/SplitLayout.java | 76 +++++++++++++++++-- .../shell/splitscreen/StageCoordinator.java | 20 ++++- .../shell/common/split/SplitLayoutTests.java | 4 +- 4 files changed, 97 insertions(+), 16 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index 94a87583da892..8405385e174ee 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -215,7 +215,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou if (mSplitLayout != null) { if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) { - onBoundsChanged(mSplitLayout); + onLayoutChanged(mSplitLayout); } // updateConfiguration re-inits the dividerbar, so show it now mSyncQueue.runInSync(t -> t.show(mSplitLayout.getDividerLeash())); @@ -299,17 +299,24 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou } @Override - public void onBoundsChanging(SplitLayout layout) { + public void onLayoutChanging(SplitLayout layout) { mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); } @Override - public void onBoundsChanged(SplitLayout layout) { + public void onLayoutChanged(SplitLayout layout) { final WindowContainerTransaction wct = new WindowContainerTransaction(); layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); } + + @Override + public void onLayoutShifted(int offsetX, int offsetY, SplitLayout layout) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + layout.applyLayoutShifted(wct, offsetX, offsetY, mTaskInfo1, mTaskInfo2); + mController.getTaskOrganizer().applyTransaction(wct); + } } 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 8adfac01b462c..b9ccd69fe707e 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 @@ -16,6 +16,8 @@ package com.android.wm.shell.common.split; +import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; +import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_TOP; @@ -224,13 +226,13 @@ public final class SplitLayout { void updateDivideBounds(int position) { updateBounds(position); mSplitWindowManager.setResizingSplits(true); - mSplitLayoutHandler.onBoundsChanging(this); + mSplitLayoutHandler.onLayoutChanging(this); } void setDividePosition(int position) { mDividePosition = position; updateBounds(mDividePosition); - mSplitLayoutHandler.onBoundsChanged(this); + mSplitLayoutHandler.onLayoutChanged(this); mSplitWindowManager.setResizingSplits(false); } @@ -352,6 +354,46 @@ public final class SplitLayout { .setBounds(task2.token, mImePositionProcessor.adjustForIme(mBounds2)); } + /** + * Shift configuration bounds to prevent client apps get configuration changed or relaunch. And + * restore shifted configuration bounds if it's no longer shifted. + */ + public void applyLayoutShifted(WindowContainerTransaction wct, int offsetX, int offsetY, + ActivityManager.RunningTaskInfo taskInfo1, ActivityManager.RunningTaskInfo taskInfo2) { + if (offsetX == 0 && offsetY == 0) { + wct.setBounds(taskInfo1.token, mBounds1); + wct.setAppBounds(taskInfo1.token, null); + wct.setScreenSizeDp(taskInfo1.token, + SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); + + wct.setBounds(taskInfo2.token, mBounds2); + wct.setAppBounds(taskInfo2.token, null); + wct.setScreenSizeDp(taskInfo2.token, + SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); + } else { + final Rect bounds = new Rect(); + bounds.set(taskInfo1.configuration.windowConfiguration.getBounds()); + bounds.offset(offsetX, offsetY); + wct.setBounds(taskInfo1.token, bounds); + bounds.set(taskInfo1.configuration.windowConfiguration.getAppBounds()); + bounds.offset(offsetX, offsetY); + wct.setAppBounds(taskInfo1.token, bounds); + wct.setScreenSizeDp(taskInfo1.token, + taskInfo1.configuration.screenWidthDp, + taskInfo1.configuration.screenHeightDp); + + bounds.set(taskInfo2.configuration.windowConfiguration.getBounds()); + bounds.offset(offsetX, offsetY); + wct.setBounds(taskInfo2.token, bounds); + bounds.set(taskInfo2.configuration.windowConfiguration.getAppBounds()); + bounds.offset(offsetX, offsetY); + wct.setAppBounds(taskInfo2.token, bounds); + wct.setScreenSizeDp(taskInfo2.token, + taskInfo2.configuration.screenWidthDp, + taskInfo2.configuration.screenHeightDp); + } + } + /** Handles layout change event. */ public interface SplitLayoutHandler { @@ -359,10 +401,18 @@ public final class SplitLayout { void onSnappedToDismiss(boolean snappedToEnd); /** Calls when the bounds is changing due to animation or dragging divider bar. */ - void onBoundsChanging(SplitLayout layout); + void onLayoutChanging(SplitLayout layout); /** Calls when the target bounds changed. */ - void onBoundsChanged(SplitLayout layout); + void onLayoutChanged(SplitLayout layout); + + /** + * Notifies when the layout shifted. So the layout handler can shift configuration + * bounds correspondingly to make sure client apps won't get configuration changed or + * relaunch. If the layout is no longer shifted, layout handler should restore shifted + * configuration bounds. + */ + void onLayoutShifted(int offsetX, int offsetY, SplitLayout layout); /** Calls when user double tapped on the divider bar. */ default void onDoubleTappedDivider() { @@ -427,6 +477,18 @@ public final class SplitLayout { && !isFloating && !isLandscape(mRootBounds) && showing; mTargetYOffset = needOffset ? getTargetYOffset() : 0; + if (mTargetYOffset != mLastYOffset) { + // Freeze the configuration size with offset to prevent app get a configuration + // changed or relaunch. This is required to make sure client apps will calculate + // insets properly after layout shifted. + if (mTargetYOffset == 0) { + mSplitLayoutHandler.onLayoutShifted(0, 0, SplitLayout.this); + } else { + mSplitLayoutHandler.onLayoutShifted(0, mTargetYOffset - mLastYOffset, + SplitLayout.this); + } + } + // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough // because DividerView won't receive onImeVisibilityChanged callback after it being @@ -441,7 +503,7 @@ public final class SplitLayout { public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { if (displayId != mDisplayId) return; onProgress(getProgress(imeTop)); - mSplitLayoutHandler.onBoundsChanging(SplitLayout.this); + mSplitLayoutHandler.onLayoutChanging(SplitLayout.this); } @Override @@ -449,7 +511,7 @@ public final class SplitLayout { SurfaceControl.Transaction t) { if (displayId != mDisplayId || cancel) return; onProgress(1.0f); - mSplitLayoutHandler.onBoundsChanging(SplitLayout.this); + mSplitLayoutHandler.onLayoutChanging(SplitLayout.this); } @Override @@ -459,7 +521,7 @@ public final class SplitLayout { if (!controlling && mImeShown) { reset(); mSplitWindowManager.setInteractive(true); - mSplitLayoutHandler.onBoundsChanging(SplitLayout.this); + mSplitLayoutHandler.onLayoutChanging(SplitLayout.this); } } 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 d1aa2d73f696d..802ae926bcf25 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 @@ -422,7 +422,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSideStageListener.mVisible && updateBounds) { if (wct == null) { // onBoundsChanged builds/applies a wct with the contents of updateWindowBounds. - onBoundsChanged(mSplitLayout); + onLayoutChanged(mSplitLayout); } else { updateWindowBounds(mSplitLayout, wct); } @@ -732,12 +732,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } @Override - public void onBoundsChanging(SplitLayout layout) { + public void onLayoutChanging(SplitLayout layout) { mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); } @Override - public void onBoundsChanged(SplitLayout layout) { + public void onLayoutChanged(SplitLayout layout) { final WindowContainerTransaction wct = new WindowContainerTransaction(); updateWindowBounds(layout, wct); mSyncQueue.queue(wct); @@ -780,6 +780,18 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return SPLIT_POSITION_UNDEFINED; } + @Override + public void onLayoutShifted(int offsetX, int offsetY, SplitLayout layout) { + final StageTaskListener topLeftStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; + final StageTaskListener bottomRightStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; + final WindowContainerTransaction wct = new WindowContainerTransaction(); + layout.applyLayoutShifted(wct, offsetX, offsetY, topLeftStage.mRootTaskInfo, + bottomRightStage.mRootTaskInfo); + mTaskOrganizer.applyTransaction(wct); + } + @Override public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) { mDisplayAreaInfo = displayAreaInfo; @@ -802,7 +814,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout != null && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration) && mMainStage.isActive()) { - onBoundsChanged(mSplitLayout); + onLayoutChanged(mSplitLayout); mSyncQueue.runInSync(t -> applyDividerVisibility(t)); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index e138595c47f38..3dc0465fbbb40 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -92,13 +92,13 @@ public class SplitLayoutTests extends ShellTestCase { @Test public void testUpdateDivideBounds() { mSplitLayout.updateDivideBounds(anyInt()); - verify(mSplitLayoutHandler).onBoundsChanging(any(SplitLayout.class)); + verify(mSplitLayoutHandler).onLayoutChanging(any(SplitLayout.class)); } @Test public void testSetDividePosition() { mSplitLayout.setDividePosition(anyInt()); - verify(mSplitLayoutHandler).onBoundsChanged(any(SplitLayout.class)); + verify(mSplitLayoutHandler).onLayoutChanged(any(SplitLayout.class)); } @Test