Loading services/core/java/com/android/server/wm/DisplayContent.java +72 −18 Original line number Diff line number Diff line Loading @@ -4505,7 +4505,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ @VisibleForTesting SurfaceControl computeImeParent() { if (mImeLayeringTarget != null) { if (!canComputeImeParent(mImeLayeringTarget, mImeInputTarget)) { return null; } // Attach it to app if the target is part of an app and such app is covering the entire // screen. If it's not covering the entire screen the IME might extend beyond the apps // bounds. if (shouldImeAttachedToApp()) { return mImeLayeringTarget.mActivityRecord.getSurfaceControl(); } // Otherwise, we just attach it to where the display area policy put it. return mImeWindowsContainer.getParent() != null ? mImeWindowsContainer.getParent().getSurfaceControl() : null; } private static boolean canComputeImeParent(@Nullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget) { if (imeLayeringTarget == null) { return false; } if (shouldComputeImeParentForEmbeddedActivity(imeLayeringTarget, imeInputTarget)) { return true; } // Ensure changing the IME parent when the layering target that may use IME has // became to the input target for preventing IME flickers. // Note that: Loading @@ -4516,23 +4537,56 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // behind the starting window may request the input. If so, then we should still hold // the IME parent change until the activity started the input. boolean imeLayeringTargetMayUseIme = LayoutParams.mayUseInputMethod(mImeLayeringTarget.mAttrs.flags) || mImeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING; if (imeLayeringTargetMayUseIme && (mImeInputTarget == null || mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord())) { LayoutParams.mayUseInputMethod(imeLayeringTarget.mAttrs.flags) || imeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING; if (imeLayeringTargetMayUseIme && (imeInputTarget == null || imeLayeringTarget.mActivityRecord != imeInputTarget.getActivityRecord())) { // Do not change parent if the window hasn't requested IME. return null; return false; } return true; } // Attach it to app if the target is part of an app and such app is covering the entire // screen. If it's not covering the entire screen the IME might extend beyond the apps // bounds. if (shouldImeAttachedToApp()) { return mImeLayeringTarget.mActivityRecord.getSurfaceControl(); /** * Called from {@link #computeImeParent()} to check the given IME targets if the IME surface * parent should be updated in ActivityEmbeddings. * * As the IME layering target is calculated according to the window hierarchy by * {@link #computeImeTarget}, the layering target and input target may be different when the * window hasn't started input connection, WindowManagerService hasn't yet received the * input target which reported from InputMethodManagerService. To make the IME surface will be * shown on the best fit IME layering target, we basically won't update IME parent until both * IME input and layering target updated for better IME transition. * * However, in activity embedding, tapping a window won't update it to the top window so the * calculated IME layering target may higher than input target. Update IME parent for this case. * * @return {@code true} means the layer of IME layering target is higher than the input target * and {@link #computeImeParent()} should keep progressing to update the IME * surface parent on the display in case the IME surface left behind. */ private static boolean shouldComputeImeParentForEmbeddedActivity( @Nullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget) { if (imeInputTarget == null || imeLayeringTarget == null) { return false; } // Otherwise, we just attach it to where the display area policy put it. return mImeWindowsContainer.getParent() != null ? mImeWindowsContainer.getParent().getSurfaceControl() : null; final WindowState inputTargetWindow = imeInputTarget.getWindowState(); if (inputTargetWindow == null || !imeLayeringTarget.isAttached() || !inputTargetWindow.isAttached()) { return false; } final ActivityRecord inputTargetRecord = imeInputTarget.getActivityRecord(); final ActivityRecord layeringTargetRecord = imeLayeringTarget.getActivityRecord(); if (inputTargetRecord == null || layeringTargetRecord == null || inputTargetRecord == layeringTargetRecord || inputTargetRecord.getTask() != layeringTargetRecord.getTask() || !inputTargetRecord.isEmbedded() || !layeringTargetRecord.isEmbedded()) { // Check whether the input target and layering target are embedded in the same Task. return false; } return imeLayeringTarget.compareTo(inputTargetWindow) > 0; } void setLayoutNeeded() { Loading services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +31 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; Loading Loading @@ -603,4 +604,34 @@ public class TaskFragmentTest extends WindowTestsBase { assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf1.getOrientation(SCREEN_ORIENTATION_UNSET)); assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, task.getOrientation(SCREEN_ORIENTATION_UNSET)); } @Test public void testUpdateImeParentForActivityEmbedding() { // Setup two activities in ActivityEmbedding. final Task task = createTask(mDisplayContent); final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) .setParentTask(task) .createActivityCount(1) .setOrganizer(mOrganizer) .setFragmentToken(new Binder()) .build(); final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) .setParentTask(task) .createActivityCount(1) .setOrganizer(mOrganizer) .setFragmentToken(new Binder()) .build(); final ActivityRecord activity0 = tf0.getTopMostActivity(); final ActivityRecord activity1 = tf1.getTopMostActivity(); final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, activity0, "win0"); final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity1, "win1"); doReturn(false).when(mDisplayContent).shouldImeAttachedToApp(); mDisplayContent.setImeInputTarget(win0); mDisplayContent.setImeLayeringTarget(win1); // The ImeParent should be the display. assertEquals(mDisplayContent.getImeContainer().getParent().getSurfaceControl(), mDisplayContent.computeImeParent()); } } Loading
services/core/java/com/android/server/wm/DisplayContent.java +72 −18 Original line number Diff line number Diff line Loading @@ -4505,7 +4505,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ @VisibleForTesting SurfaceControl computeImeParent() { if (mImeLayeringTarget != null) { if (!canComputeImeParent(mImeLayeringTarget, mImeInputTarget)) { return null; } // Attach it to app if the target is part of an app and such app is covering the entire // screen. If it's not covering the entire screen the IME might extend beyond the apps // bounds. if (shouldImeAttachedToApp()) { return mImeLayeringTarget.mActivityRecord.getSurfaceControl(); } // Otherwise, we just attach it to where the display area policy put it. return mImeWindowsContainer.getParent() != null ? mImeWindowsContainer.getParent().getSurfaceControl() : null; } private static boolean canComputeImeParent(@Nullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget) { if (imeLayeringTarget == null) { return false; } if (shouldComputeImeParentForEmbeddedActivity(imeLayeringTarget, imeInputTarget)) { return true; } // Ensure changing the IME parent when the layering target that may use IME has // became to the input target for preventing IME flickers. // Note that: Loading @@ -4516,23 +4537,56 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // behind the starting window may request the input. If so, then we should still hold // the IME parent change until the activity started the input. boolean imeLayeringTargetMayUseIme = LayoutParams.mayUseInputMethod(mImeLayeringTarget.mAttrs.flags) || mImeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING; if (imeLayeringTargetMayUseIme && (mImeInputTarget == null || mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord())) { LayoutParams.mayUseInputMethod(imeLayeringTarget.mAttrs.flags) || imeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING; if (imeLayeringTargetMayUseIme && (imeInputTarget == null || imeLayeringTarget.mActivityRecord != imeInputTarget.getActivityRecord())) { // Do not change parent if the window hasn't requested IME. return null; return false; } return true; } // Attach it to app if the target is part of an app and such app is covering the entire // screen. If it's not covering the entire screen the IME might extend beyond the apps // bounds. if (shouldImeAttachedToApp()) { return mImeLayeringTarget.mActivityRecord.getSurfaceControl(); /** * Called from {@link #computeImeParent()} to check the given IME targets if the IME surface * parent should be updated in ActivityEmbeddings. * * As the IME layering target is calculated according to the window hierarchy by * {@link #computeImeTarget}, the layering target and input target may be different when the * window hasn't started input connection, WindowManagerService hasn't yet received the * input target which reported from InputMethodManagerService. To make the IME surface will be * shown on the best fit IME layering target, we basically won't update IME parent until both * IME input and layering target updated for better IME transition. * * However, in activity embedding, tapping a window won't update it to the top window so the * calculated IME layering target may higher than input target. Update IME parent for this case. * * @return {@code true} means the layer of IME layering target is higher than the input target * and {@link #computeImeParent()} should keep progressing to update the IME * surface parent on the display in case the IME surface left behind. */ private static boolean shouldComputeImeParentForEmbeddedActivity( @Nullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget) { if (imeInputTarget == null || imeLayeringTarget == null) { return false; } // Otherwise, we just attach it to where the display area policy put it. return mImeWindowsContainer.getParent() != null ? mImeWindowsContainer.getParent().getSurfaceControl() : null; final WindowState inputTargetWindow = imeInputTarget.getWindowState(); if (inputTargetWindow == null || !imeLayeringTarget.isAttached() || !inputTargetWindow.isAttached()) { return false; } final ActivityRecord inputTargetRecord = imeInputTarget.getActivityRecord(); final ActivityRecord layeringTargetRecord = imeLayeringTarget.getActivityRecord(); if (inputTargetRecord == null || layeringTargetRecord == null || inputTargetRecord == layeringTargetRecord || inputTargetRecord.getTask() != layeringTargetRecord.getTask() || !inputTargetRecord.isEmbedded() || !layeringTargetRecord.isEmbedded()) { // Check whether the input target and layering target are embedded in the same Task. return false; } return imeLayeringTarget.compareTo(inputTargetWindow) > 0; } void setLayoutNeeded() { Loading
services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +31 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; Loading Loading @@ -603,4 +604,34 @@ public class TaskFragmentTest extends WindowTestsBase { assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf1.getOrientation(SCREEN_ORIENTATION_UNSET)); assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, task.getOrientation(SCREEN_ORIENTATION_UNSET)); } @Test public void testUpdateImeParentForActivityEmbedding() { // Setup two activities in ActivityEmbedding. final Task task = createTask(mDisplayContent); final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) .setParentTask(task) .createActivityCount(1) .setOrganizer(mOrganizer) .setFragmentToken(new Binder()) .build(); final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) .setParentTask(task) .createActivityCount(1) .setOrganizer(mOrganizer) .setFragmentToken(new Binder()) .build(); final ActivityRecord activity0 = tf0.getTopMostActivity(); final ActivityRecord activity1 = tf1.getTopMostActivity(); final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, activity0, "win0"); final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity1, "win1"); doReturn(false).when(mDisplayContent).shouldImeAttachedToApp(); mDisplayContent.setImeInputTarget(win0); mDisplayContent.setImeLayeringTarget(win1); // The ImeParent should be the display. assertEquals(mDisplayContent.getImeContainer().getParent().getSurfaceControl(), mDisplayContent.computeImeParent()); } }