Loading core/java/android/window/WindowContainerTransaction.java +43 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TAS import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.app.PendingIntent; import android.app.WindowConfiguration; Loading @@ -44,6 +45,7 @@ import android.util.ArrayMap; import android.view.InsetsFrameProvider; import android.view.InsetsSource; import android.view.SurfaceControl; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; import com.android.window.flags.Flags; Loading Loading @@ -266,6 +268,23 @@ public final class WindowContainerTransaction implements Parcelable { return this; } /** * Sets whether the IME insets should be excluded by {@link com.android.server.wm.InsetsPolicy}. * @hide */ @SuppressLint("UnflaggedApi") @NonNull public WindowContainerTransaction setExcludeImeInsets( @NonNull WindowContainerToken container, boolean exclude) { final HierarchyOp hierarchyOp = new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES) .setContainer(container.asBinder()) .setExcludeInsetsTypes(exclude ? WindowInsets.Type.ime() : 0) .build(); mHierarchyOps.add(hierarchyOp); return this; } /** * Sets whether a container or its children should be hidden. When {@code false}, the existing * visibility of the container applies, but when {@code true} the container will be forced Loading Loading @@ -1449,6 +1468,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK = 18; public static final int HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE = 19; public static final int HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION = 20; public static final int HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES = 21; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. Loading Loading @@ -1512,6 +1532,8 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mIsTrimmableFromRecents; private @InsetsType int mExcludeInsetsTypes; public static HierarchyOp createForReparent( @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT) Loading Loading @@ -1649,6 +1671,7 @@ public final class WindowContainerTransaction implements Parcelable { mAlwaysOnTop = copy.mAlwaysOnTop; mReparentLeafTaskIfRelaunch = copy.mReparentLeafTaskIfRelaunch; mIsTrimmableFromRecents = copy.mIsTrimmableFromRecents; mExcludeInsetsTypes = copy.mExcludeInsetsTypes; } protected HierarchyOp(Parcel in) { Loading @@ -1671,6 +1694,7 @@ public final class WindowContainerTransaction implements Parcelable { mAlwaysOnTop = in.readBoolean(); mReparentLeafTaskIfRelaunch = in.readBoolean(); mIsTrimmableFromRecents = in.readBoolean(); mExcludeInsetsTypes = in.readInt(); } public int getType() { Loading Loading @@ -1772,6 +1796,10 @@ public final class WindowContainerTransaction implements Parcelable { return mIsTrimmableFromRecents; } public @InsetsType int getExcludeInsetsTypes() { return mExcludeInsetsTypes; } /** Gets a string representation of a hierarchy-op type. */ public static String hopToString(int type) { switch (type) { Loading @@ -1795,6 +1823,7 @@ public final class WindowContainerTransaction implements Parcelable { return "setReparentLeafTaskIfRelaunch"; case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION: return "addTaskFragmentOperation"; case HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES: return "setExcludeInsetsTypes"; default: return "HOP(" + type + ")"; } } Loading Loading @@ -1868,6 +1897,11 @@ public final class WindowContainerTransaction implements Parcelable { sb.append("fragmentToken= ").append(mContainer) .append(" operation= ").append(mTaskFragmentOperation); break; case HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES: sb.append("container= ").append(mContainer) .append(" mExcludeInsetsTypes= ") .append(WindowInsets.Type.toString(mExcludeInsetsTypes)); break; case HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE: sb.append("container= ").append(mContainer) .append(" isTrimmable= ") Loading Loading @@ -1903,6 +1937,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeBoolean(mAlwaysOnTop); dest.writeBoolean(mReparentLeafTaskIfRelaunch); dest.writeBoolean(mIsTrimmableFromRecents); dest.writeInt(mExcludeInsetsTypes); } @Override Loading Loading @@ -1974,6 +2009,8 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mIsTrimmableFromRecents; private @InsetsType int mExcludeInsetsTypes; Builder(int type) { mType = type; } Loading Loading @@ -2069,6 +2106,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } Builder setExcludeInsetsTypes(@InsetsType int excludeInsetsTypes) { mExcludeInsetsTypes = excludeInsetsTypes; return this; } HierarchyOp build() { final HierarchyOp hierarchyOp = new HierarchyOp(mType); hierarchyOp.mContainer = mContainer; Loading @@ -2093,6 +2135,7 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mIncludingParents = mIncludingParents; hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch; hierarchyOp.mIsTrimmableFromRecents = mIsTrimmableFromRecents; hierarchyOp.mExcludeInsetsTypes = mExcludeInsetsTypes; return hierarchyOp; } Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +20 −0 Original line number Diff line number Diff line Loading @@ -157,6 +157,14 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } private void dispatchImeRequested(int displayId, boolean isRequested) { synchronized (mPositionProcessors) { for (ImePositionProcessor pp : mPositionProcessors) { pp.onImeRequested(displayId, isRequested); } } } @ImePositionProcessor.ImeAnimationFlags private int dispatchStartPositioning(int displayId, int hiddenTop, int shownTop, boolean show, boolean isFloating, SurfaceControl.Transaction t) { Loading Loading @@ -398,6 +406,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged public void setImeInputTargetRequestedVisibility(boolean visible) { if (android.view.inputmethod.Flags.refactorInsetsController()) { mImeRequestedVisible = visible; dispatchImeRequested(mDisplayId, mImeRequestedVisible); // In the case that the IME becomes visible, but we have the control with leash // already (e.g., when focussing an editText in activity B, while and editText in // activity A is focussed), we will not get a call of #insetsControlChanged, and Loading Loading @@ -446,6 +456,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (imeSource == null || mImeSourceControl == null) { return; } // TODO(b/353463205): For hide: this still has the statsToken from the previous show // request final var statsToken = mImeSourceControl.getImeStatsToken(); startAnimation(show, forceRestart, statsToken); Loading Loading @@ -705,6 +717,14 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged @interface ImeAnimationFlags { } /** * Called when the IME was requested by an app * * @param isRequested {@code true} if the IME was requested to be visible */ default void onImeRequested(int displayId, boolean isRequested) { } /** * Called when the IME position is starting to animate. * Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +29 −0 Original line number Diff line number Diff line Loading @@ -1106,6 +1106,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange default void onDoubleTappedDivider() { } /** * Sets the excludedInsetsTypes for the IME in the root WindowContainer. */ void setExcludeImeInsets(boolean exclude); /** Returns split position of the token. */ @SplitPosition int getSplitItemPosition(WindowContainerToken token); Loading Loading @@ -1304,6 +1309,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mDisplayId = displayId; } @Override public void onImeRequested(int displayId, boolean isRequested) { if (displayId != mDisplayId) return; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "IME was set to requested=%s", isRequested); mSplitLayoutHandler.setExcludeImeInsets(true); } @Override public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, boolean showing, boolean isFloating, SurfaceControl.Transaction t) { Loading Loading @@ -1356,6 +1369,12 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange setDividerInteractive(!mImeShown || !mHasImeFocus || isFloating, true, "onImeStartPositioning"); if (android.view.inputmethod.Flags.refactorInsetsController()) { if (mImeShown) { mSplitLayoutHandler.setExcludeImeInsets(false); } } return mTargetYOffset != mLastYOffset ? IME_ANIMATION_NO_ALPHA : 0; } Loading @@ -1374,6 +1393,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange "Split IME animation ending, canceled=%b", cancel); onProgress(1.0f); mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this); if (android.view.inputmethod.Flags.refactorInsetsController()) { if (!mImeShown) { // The IME hide animation is started immediately and at that point, the IME // insets are not yet set to hidden. Therefore only resetting the // excludedTypes at the end of the animation. Note: InsetsPolicy will only // set the IME height to zero, when it is visible. When it becomes invisible, // we dispatch the insets (the height there is zero as well) mSplitLayoutHandler.setExcludeImeInsets(false); } } } @Override Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +18 −0 Original line number Diff line number Diff line Loading @@ -901,6 +901,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setEnterInstanceId(instanceId); } @Override public void setExcludeImeInsets(boolean exclude) { if (android.view.inputmethod.Flags.refactorInsetsController()) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (mRootTaskInfo == null) { ProtoLog.e(WM_SHELL_SPLIT_SCREEN, "setExcludeImeInsets: mRootTaskInfo is null"); return; } ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setExcludeImeInsets: root taskId=%s exclude=%s", mRootTaskInfo.taskId, exclude); wct.setExcludeImeInsets(mRootTaskInfo.token, exclude); mTaskOrganizer.applyTransaction(wct); } } /** * Checks if either of the apps in the desired split launch is currently in Pip. If so, it will * launch the non-pipped app as a fullscreen app, otherwise no-op. Loading Loading @@ -1717,6 +1734,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true); wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true); // Make the stages adjacent to each other so they occlude what's behind them. wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); setRootForceTranslucent(true, wct); Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +39 −3 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; Loading @@ -34,6 +36,12 @@ import static org.mockito.Mockito.verifyZeroInteractions; import android.graphics.Insets; import android.graphics.Point; import android.os.Looper; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.view.IWindowManager; import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; Loading @@ -47,6 +55,7 @@ import com.android.wm.shell.shared.TransactionPool; import com.android.wm.shell.sysui.ShellInit; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; Loading @@ -61,11 +70,16 @@ import java.util.concurrent.Executor; */ @SmallTest public class DisplayImeControllerTest extends ShellTestCase { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock private SurfaceControl.Transaction mT; @Mock private ShellInit mShellInit; @Mock private IWindowManager mWm; private DisplayImeController mDisplayImeController; private DisplayImeController.PerDisplay mPerDisplay; private Executor mExecutor; Loading @@ -73,7 +87,8 @@ public class DisplayImeControllerTest extends ShellTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mExecutor = spy(Runnable::run); mPerDisplay = new DisplayImeController(null, mShellInit, null, null, new TransactionPool() { mDisplayImeController = new DisplayImeController(mWm, mShellInit, null, null, new TransactionPool() { @Override public SurfaceControl.Transaction acquire() { return mT; Loading @@ -84,8 +99,10 @@ public class DisplayImeControllerTest extends ShellTestCase { } }, mExecutor) { @Override void removeImeSurface(int displayId) { } }.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0); void removeImeSurface(int displayId) { } }; mPerDisplay = mDisplayImeController.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0); } @Test Loading @@ -95,12 +112,14 @@ public class DisplayImeControllerTest extends ShellTestCase { @Test public void insetsControlChanged_schedulesNoWorkOnExecutor() { Looper.prepare(); mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl()); verifyZeroInteractions(mExecutor); } @Test public void insetsChanged_schedulesNoWorkOnExecutor() { Looper.prepare(); mPerDisplay.insetsChanged(insetsStateWithIme(false)); verifyZeroInteractions(mExecutor); } Loading @@ -117,7 +136,10 @@ public class DisplayImeControllerTest extends ShellTestCase { verifyZeroInteractions(mExecutor); } // With the refactor, the control's isInitiallyVisible is used to apply to the IME, therefore // this test is obsolete @Test @RequiresFlagsDisabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER) public void reappliesVisibilityToChangedLeash() { verifyZeroInteractions(mT); mPerDisplay.mImeShowing = false; Loading @@ -136,6 +158,7 @@ public class DisplayImeControllerTest extends ShellTestCase { @Test public void insetsControlChanged_updateImeSourceControl() { Looper.prepare(); mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl()); assertNotNull(mPerDisplay.mImeSourceControl); Loading @@ -143,6 +166,19 @@ public class DisplayImeControllerTest extends ShellTestCase { assertNull(mPerDisplay.mImeSourceControl); } @Test @RequiresFlagsEnabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER) public void setImeInputTargetRequestedVisibility_invokeOnImeRequested() { var mockPp = mock(DisplayImeController.ImePositionProcessor.class); mDisplayImeController.addPositionProcessor(mockPp); mPerDisplay.setImeInputTargetRequestedVisibility(true); verify(mockPp).onImeRequested(anyInt(), eq(true)); mPerDisplay.setImeInputTargetRequestedVisibility(false); verify(mockPp).onImeRequested(anyInt(), eq(false)); } private InsetsSourceControl[] insetsSourceControl() { return new InsetsSourceControl[]{ new InsetsSourceControl( Loading Loading
core/java/android/window/WindowContainerTransaction.java +43 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TAS import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.app.PendingIntent; import android.app.WindowConfiguration; Loading @@ -44,6 +45,7 @@ import android.util.ArrayMap; import android.view.InsetsFrameProvider; import android.view.InsetsSource; import android.view.SurfaceControl; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; import com.android.window.flags.Flags; Loading Loading @@ -266,6 +268,23 @@ public final class WindowContainerTransaction implements Parcelable { return this; } /** * Sets whether the IME insets should be excluded by {@link com.android.server.wm.InsetsPolicy}. * @hide */ @SuppressLint("UnflaggedApi") @NonNull public WindowContainerTransaction setExcludeImeInsets( @NonNull WindowContainerToken container, boolean exclude) { final HierarchyOp hierarchyOp = new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES) .setContainer(container.asBinder()) .setExcludeInsetsTypes(exclude ? WindowInsets.Type.ime() : 0) .build(); mHierarchyOps.add(hierarchyOp); return this; } /** * Sets whether a container or its children should be hidden. When {@code false}, the existing * visibility of the container applies, but when {@code true} the container will be forced Loading Loading @@ -1449,6 +1468,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK = 18; public static final int HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE = 19; public static final int HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION = 20; public static final int HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES = 21; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. Loading Loading @@ -1512,6 +1532,8 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mIsTrimmableFromRecents; private @InsetsType int mExcludeInsetsTypes; public static HierarchyOp createForReparent( @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT) Loading Loading @@ -1649,6 +1671,7 @@ public final class WindowContainerTransaction implements Parcelable { mAlwaysOnTop = copy.mAlwaysOnTop; mReparentLeafTaskIfRelaunch = copy.mReparentLeafTaskIfRelaunch; mIsTrimmableFromRecents = copy.mIsTrimmableFromRecents; mExcludeInsetsTypes = copy.mExcludeInsetsTypes; } protected HierarchyOp(Parcel in) { Loading @@ -1671,6 +1694,7 @@ public final class WindowContainerTransaction implements Parcelable { mAlwaysOnTop = in.readBoolean(); mReparentLeafTaskIfRelaunch = in.readBoolean(); mIsTrimmableFromRecents = in.readBoolean(); mExcludeInsetsTypes = in.readInt(); } public int getType() { Loading Loading @@ -1772,6 +1796,10 @@ public final class WindowContainerTransaction implements Parcelable { return mIsTrimmableFromRecents; } public @InsetsType int getExcludeInsetsTypes() { return mExcludeInsetsTypes; } /** Gets a string representation of a hierarchy-op type. */ public static String hopToString(int type) { switch (type) { Loading @@ -1795,6 +1823,7 @@ public final class WindowContainerTransaction implements Parcelable { return "setReparentLeafTaskIfRelaunch"; case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION: return "addTaskFragmentOperation"; case HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES: return "setExcludeInsetsTypes"; default: return "HOP(" + type + ")"; } } Loading Loading @@ -1868,6 +1897,11 @@ public final class WindowContainerTransaction implements Parcelable { sb.append("fragmentToken= ").append(mContainer) .append(" operation= ").append(mTaskFragmentOperation); break; case HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES: sb.append("container= ").append(mContainer) .append(" mExcludeInsetsTypes= ") .append(WindowInsets.Type.toString(mExcludeInsetsTypes)); break; case HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE: sb.append("container= ").append(mContainer) .append(" isTrimmable= ") Loading Loading @@ -1903,6 +1937,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeBoolean(mAlwaysOnTop); dest.writeBoolean(mReparentLeafTaskIfRelaunch); dest.writeBoolean(mIsTrimmableFromRecents); dest.writeInt(mExcludeInsetsTypes); } @Override Loading Loading @@ -1974,6 +2009,8 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mIsTrimmableFromRecents; private @InsetsType int mExcludeInsetsTypes; Builder(int type) { mType = type; } Loading Loading @@ -2069,6 +2106,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } Builder setExcludeInsetsTypes(@InsetsType int excludeInsetsTypes) { mExcludeInsetsTypes = excludeInsetsTypes; return this; } HierarchyOp build() { final HierarchyOp hierarchyOp = new HierarchyOp(mType); hierarchyOp.mContainer = mContainer; Loading @@ -2093,6 +2135,7 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mIncludingParents = mIncludingParents; hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch; hierarchyOp.mIsTrimmableFromRecents = mIsTrimmableFromRecents; hierarchyOp.mExcludeInsetsTypes = mExcludeInsetsTypes; return hierarchyOp; } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +20 −0 Original line number Diff line number Diff line Loading @@ -157,6 +157,14 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } private void dispatchImeRequested(int displayId, boolean isRequested) { synchronized (mPositionProcessors) { for (ImePositionProcessor pp : mPositionProcessors) { pp.onImeRequested(displayId, isRequested); } } } @ImePositionProcessor.ImeAnimationFlags private int dispatchStartPositioning(int displayId, int hiddenTop, int shownTop, boolean show, boolean isFloating, SurfaceControl.Transaction t) { Loading Loading @@ -398,6 +406,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged public void setImeInputTargetRequestedVisibility(boolean visible) { if (android.view.inputmethod.Flags.refactorInsetsController()) { mImeRequestedVisible = visible; dispatchImeRequested(mDisplayId, mImeRequestedVisible); // In the case that the IME becomes visible, but we have the control with leash // already (e.g., when focussing an editText in activity B, while and editText in // activity A is focussed), we will not get a call of #insetsControlChanged, and Loading Loading @@ -446,6 +456,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (imeSource == null || mImeSourceControl == null) { return; } // TODO(b/353463205): For hide: this still has the statsToken from the previous show // request final var statsToken = mImeSourceControl.getImeStatsToken(); startAnimation(show, forceRestart, statsToken); Loading Loading @@ -705,6 +717,14 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged @interface ImeAnimationFlags { } /** * Called when the IME was requested by an app * * @param isRequested {@code true} if the IME was requested to be visible */ default void onImeRequested(int displayId, boolean isRequested) { } /** * Called when the IME position is starting to animate. * Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +29 −0 Original line number Diff line number Diff line Loading @@ -1106,6 +1106,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange default void onDoubleTappedDivider() { } /** * Sets the excludedInsetsTypes for the IME in the root WindowContainer. */ void setExcludeImeInsets(boolean exclude); /** Returns split position of the token. */ @SplitPosition int getSplitItemPosition(WindowContainerToken token); Loading Loading @@ -1304,6 +1309,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mDisplayId = displayId; } @Override public void onImeRequested(int displayId, boolean isRequested) { if (displayId != mDisplayId) return; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "IME was set to requested=%s", isRequested); mSplitLayoutHandler.setExcludeImeInsets(true); } @Override public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, boolean showing, boolean isFloating, SurfaceControl.Transaction t) { Loading Loading @@ -1356,6 +1369,12 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange setDividerInteractive(!mImeShown || !mHasImeFocus || isFloating, true, "onImeStartPositioning"); if (android.view.inputmethod.Flags.refactorInsetsController()) { if (mImeShown) { mSplitLayoutHandler.setExcludeImeInsets(false); } } return mTargetYOffset != mLastYOffset ? IME_ANIMATION_NO_ALPHA : 0; } Loading @@ -1374,6 +1393,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange "Split IME animation ending, canceled=%b", cancel); onProgress(1.0f); mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this); if (android.view.inputmethod.Flags.refactorInsetsController()) { if (!mImeShown) { // The IME hide animation is started immediately and at that point, the IME // insets are not yet set to hidden. Therefore only resetting the // excludedTypes at the end of the animation. Note: InsetsPolicy will only // set the IME height to zero, when it is visible. When it becomes invisible, // we dispatch the insets (the height there is zero as well) mSplitLayoutHandler.setExcludeImeInsets(false); } } } @Override Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +18 −0 Original line number Diff line number Diff line Loading @@ -901,6 +901,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setEnterInstanceId(instanceId); } @Override public void setExcludeImeInsets(boolean exclude) { if (android.view.inputmethod.Flags.refactorInsetsController()) { final WindowContainerTransaction wct = new WindowContainerTransaction(); if (mRootTaskInfo == null) { ProtoLog.e(WM_SHELL_SPLIT_SCREEN, "setExcludeImeInsets: mRootTaskInfo is null"); return; } ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setExcludeImeInsets: root taskId=%s exclude=%s", mRootTaskInfo.taskId, exclude); wct.setExcludeImeInsets(mRootTaskInfo.token, exclude); mTaskOrganizer.applyTransaction(wct); } } /** * Checks if either of the apps in the desired split launch is currently in Pip. If so, it will * launch the non-pipped app as a fullscreen app, otherwise no-op. Loading Loading @@ -1717,6 +1734,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true); wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true); // Make the stages adjacent to each other so they occlude what's behind them. wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); setRootForceTranslucent(true, wct); Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +39 −3 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; Loading @@ -34,6 +36,12 @@ import static org.mockito.Mockito.verifyZeroInteractions; import android.graphics.Insets; import android.graphics.Point; import android.os.Looper; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.view.IWindowManager; import android.view.InsetsSource; import android.view.InsetsSourceControl; import android.view.InsetsState; Loading @@ -47,6 +55,7 @@ import com.android.wm.shell.shared.TransactionPool; import com.android.wm.shell.sysui.ShellInit; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; Loading @@ -61,11 +70,16 @@ import java.util.concurrent.Executor; */ @SmallTest public class DisplayImeControllerTest extends ShellTestCase { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock private SurfaceControl.Transaction mT; @Mock private ShellInit mShellInit; @Mock private IWindowManager mWm; private DisplayImeController mDisplayImeController; private DisplayImeController.PerDisplay mPerDisplay; private Executor mExecutor; Loading @@ -73,7 +87,8 @@ public class DisplayImeControllerTest extends ShellTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mExecutor = spy(Runnable::run); mPerDisplay = new DisplayImeController(null, mShellInit, null, null, new TransactionPool() { mDisplayImeController = new DisplayImeController(mWm, mShellInit, null, null, new TransactionPool() { @Override public SurfaceControl.Transaction acquire() { return mT; Loading @@ -84,8 +99,10 @@ public class DisplayImeControllerTest extends ShellTestCase { } }, mExecutor) { @Override void removeImeSurface(int displayId) { } }.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0); void removeImeSurface(int displayId) { } }; mPerDisplay = mDisplayImeController.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0); } @Test Loading @@ -95,12 +112,14 @@ public class DisplayImeControllerTest extends ShellTestCase { @Test public void insetsControlChanged_schedulesNoWorkOnExecutor() { Looper.prepare(); mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl()); verifyZeroInteractions(mExecutor); } @Test public void insetsChanged_schedulesNoWorkOnExecutor() { Looper.prepare(); mPerDisplay.insetsChanged(insetsStateWithIme(false)); verifyZeroInteractions(mExecutor); } Loading @@ -117,7 +136,10 @@ public class DisplayImeControllerTest extends ShellTestCase { verifyZeroInteractions(mExecutor); } // With the refactor, the control's isInitiallyVisible is used to apply to the IME, therefore // this test is obsolete @Test @RequiresFlagsDisabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER) public void reappliesVisibilityToChangedLeash() { verifyZeroInteractions(mT); mPerDisplay.mImeShowing = false; Loading @@ -136,6 +158,7 @@ public class DisplayImeControllerTest extends ShellTestCase { @Test public void insetsControlChanged_updateImeSourceControl() { Looper.prepare(); mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl()); assertNotNull(mPerDisplay.mImeSourceControl); Loading @@ -143,6 +166,19 @@ public class DisplayImeControllerTest extends ShellTestCase { assertNull(mPerDisplay.mImeSourceControl); } @Test @RequiresFlagsEnabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER) public void setImeInputTargetRequestedVisibility_invokeOnImeRequested() { var mockPp = mock(DisplayImeController.ImePositionProcessor.class); mDisplayImeController.addPositionProcessor(mockPp); mPerDisplay.setImeInputTargetRequestedVisibility(true); verify(mockPp).onImeRequested(anyInt(), eq(true)); mPerDisplay.setImeInputTargetRequestedVisibility(false); verify(mockPp).onImeRequested(anyInt(), eq(false)); } private InsetsSourceControl[] insetsSourceControl() { return new InsetsSourceControl[]{ new InsetsSourceControl( Loading