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
This commit is contained in:
Jerry Chang
2021-07-15 02:52:15 +00:00
parent ae5d3b43ae
commit da44526b9b
4 changed files with 97 additions and 16 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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