Merge "Improve InsetsPolicy#adjustVisibilityForIme when switching apps" into sc-v2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e3c1ec9532
@@ -675,6 +675,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
/** Whether the IME is showing when transitioning away from this activity. */
|
||||
boolean mLastImeShown;
|
||||
|
||||
/**
|
||||
* When set to true, the IME insets will be frozen until the next app becomes IME input target.
|
||||
* @see InsetsPolicy#adjustVisibilityForIme
|
||||
*/
|
||||
boolean mImeInsetsFrozenUntilStartInput;
|
||||
|
||||
/**
|
||||
* A flag to determine if this AR is in the process of closing or entering PIP. This is needed
|
||||
* to help AR know that the app is in the process of closing but hasn't yet started closing on
|
||||
@@ -1460,6 +1466,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
associateStartingDataWithTask();
|
||||
overrideConfigurationPropagation(mStartingWindow, task);
|
||||
}
|
||||
mImeInsetsFrozenUntilStartInput = false;
|
||||
}
|
||||
|
||||
if (rootTask != null && rootTask.topRunningActivity() == this) {
|
||||
@@ -4938,6 +4945,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
&& imeInputTarget.getWindow().mActivityRecord == this
|
||||
&& mDisplayContent.mInputMethodWindow != null
|
||||
&& mDisplayContent.mInputMethodWindow.isVisible();
|
||||
mImeInsetsFrozenUntilStartInput = true;
|
||||
}
|
||||
|
||||
final DisplayContent displayContent = getDisplayContent();
|
||||
@@ -6069,6 +6077,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
// closing activity having to wait until idle timeout to be stopped or destroyed if the
|
||||
// next activity won't report idle (e.g. repeated view animation).
|
||||
mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
|
||||
|
||||
// If the activity is visible, but no windows are eligible to start input, unfreeze
|
||||
// to avoid permanently frozen IME insets.
|
||||
if (mImeInsetsFrozenUntilStartInput && getWindow(
|
||||
win -> WindowManager.LayoutParams.mayUseInputMethod(win.mAttrs.flags))
|
||||
== null) {
|
||||
mImeInsetsFrozenUntilStartInput = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8053,6 +8069,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void onResize() {
|
||||
// Reset freezing IME insets flag when the activity resized.
|
||||
mImeInsetsFrozenUntilStartInput = false;
|
||||
super.onResize();
|
||||
}
|
||||
|
||||
/** Returns true if the configuration is compatible with this activity. */
|
||||
boolean isConfigurationCompatible(Configuration config) {
|
||||
final int orientation = getRequestedOrientation();
|
||||
|
||||
@@ -4022,6 +4022,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
void updateImeInputAndControlTarget(WindowState target) {
|
||||
if (mImeInputTarget != target) {
|
||||
ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
|
||||
if (target != null && target.mActivityRecord != null) {
|
||||
target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
|
||||
}
|
||||
setImeInputTarget(target);
|
||||
updateImeControlTarget();
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ class InsetsPolicy {
|
||||
InsetsState getInsetsForWindow(WindowState target) {
|
||||
final InsetsState originalState = mStateController.getInsetsForWindow(target);
|
||||
final InsetsState state = adjustVisibilityForTransientTypes(originalState);
|
||||
return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state;
|
||||
return adjustVisibilityForIme(target, state, state == originalState);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,16 +243,37 @@ class InsetsPolicy {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Navigation bar insets is always visible to IME.
|
||||
private static InsetsState adjustVisibilityForIme(InsetsState originalState,
|
||||
private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
|
||||
boolean copyState) {
|
||||
final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
|
||||
if (originalNavSource != null && !originalNavSource.isVisible()) {
|
||||
final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
|
||||
final InsetsSource navSource = new InsetsSource(originalNavSource);
|
||||
navSource.setVisible(true);
|
||||
state.addSource(navSource);
|
||||
return state;
|
||||
if (w.mIsImWindow) {
|
||||
// Navigation bar insets is always visible to IME.
|
||||
final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
|
||||
if (originalNavSource != null && !originalNavSource.isVisible()) {
|
||||
final InsetsState state = copyState ? new InsetsState(originalState)
|
||||
: originalState;
|
||||
final InsetsSource navSource = new InsetsSource(originalNavSource);
|
||||
navSource.setVisible(true);
|
||||
state.addSource(navSource);
|
||||
return state;
|
||||
}
|
||||
} else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) {
|
||||
// During switching tasks with gestural navigation, if the IME is attached to
|
||||
// one app window on that time, even the next app window is behind the IME window,
|
||||
// conceptually the window should not receive the IME insets if the next window is
|
||||
// not eligible IME requester and ready to show IME on top of it.
|
||||
final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp();
|
||||
final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME);
|
||||
|
||||
if (shouldImeAttachedToApp && originalImeSource != null) {
|
||||
final boolean imeVisibility =
|
||||
w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME);
|
||||
final InsetsState state = copyState ? new InsetsState(originalState)
|
||||
: originalState;
|
||||
final InsetsSource imeSource = new InsetsSource(originalImeSource);
|
||||
imeSource.setVisible(imeVisibility);
|
||||
state.addSource(imeSource);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return originalState;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import static android.os.Process.NOBODY_UID;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
|
||||
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
|
||||
@@ -2907,6 +2908,73 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImeInsetsFrozenFlag_resetWhenReparented() {
|
||||
final ActivityRecord activity = createActivityWithTask();
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, activity, "app");
|
||||
final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
|
||||
final Task newTask = new TaskBuilder(mSupervisor).build();
|
||||
makeWindowVisible(app, imeWindow);
|
||||
mDisplayContent.mInputMethodWindow = imeWindow;
|
||||
mDisplayContent.setImeLayeringTarget(app);
|
||||
mDisplayContent.setImeInputTarget(app);
|
||||
|
||||
// Simulate app is closing and expect the last IME is shown and IME insets is frozen.
|
||||
app.mActivityRecord.commitVisibility(false, false);
|
||||
assertTrue(app.mActivityRecord.mLastImeShown);
|
||||
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
|
||||
|
||||
// Expect IME insets frozen state will reset when the activity is reparent to the new task.
|
||||
activity.setState(RESUMED, "test");
|
||||
activity.reparent(newTask, 0 /* top */, "test");
|
||||
assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
|
||||
}
|
||||
|
||||
@UseTestDisplay(addWindows = W_INPUT_METHOD)
|
||||
@Test
|
||||
public void testImeInsetsFrozenFlag_resetWhenResized() {
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
|
||||
makeWindowVisibleAndDrawn(app, mImeWindow);
|
||||
mDisplayContent.setImeLayeringTarget(app);
|
||||
mDisplayContent.setImeInputTarget(app);
|
||||
|
||||
// Simulate app is closing and expect the last IME is shown and IME insets is frozen.
|
||||
app.mActivityRecord.commitVisibility(false, false);
|
||||
assertTrue(app.mActivityRecord.mLastImeShown);
|
||||
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
|
||||
|
||||
// Expect IME insets frozen state will reset when the activity is reparent to the new task.
|
||||
app.mActivityRecord.onResize();
|
||||
assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
|
||||
}
|
||||
|
||||
@UseTestDisplay(addWindows = W_INPUT_METHOD)
|
||||
@Test
|
||||
public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() {
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
|
||||
makeWindowVisibleAndDrawn(app, mImeWindow);
|
||||
mDisplayContent.setImeLayeringTarget(app);
|
||||
mDisplayContent.setImeInputTarget(app);
|
||||
|
||||
// Simulate app is closing and expect the last IME is shown and IME insets is frozen.
|
||||
app.mActivityRecord.commitVisibility(false, false);
|
||||
app.mActivityRecord.onWindowsGone();
|
||||
|
||||
assertTrue(app.mActivityRecord.mLastImeShown);
|
||||
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
|
||||
|
||||
// Expect IME insets frozen state will reset when the activity has no IME focusable window.
|
||||
app.mActivityRecord.forAllWindowsUnchecked(w -> {
|
||||
w.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
|
||||
return true;
|
||||
}, true);
|
||||
|
||||
app.mActivityRecord.commitVisibility(true, false);
|
||||
app.mActivityRecord.onWindowsVisible();
|
||||
|
||||
assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
|
||||
}
|
||||
|
||||
private void assertHasStartingWindow(ActivityRecord atoken) {
|
||||
assertNotNull(atoken.mStartingSurface);
|
||||
assertNotNull(atoken.mStartingData);
|
||||
|
||||
@@ -258,6 +258,7 @@ public class SystemServicesTestRule implements TestRule {
|
||||
final ActivityManagerInternal amInternal = mAmService.mInternal;
|
||||
spyOn(amInternal);
|
||||
doNothing().when(amInternal).trimApplications();
|
||||
doNothing().when(amInternal).scheduleAppGcs();
|
||||
doNothing().when(amInternal).updateCpuStats();
|
||||
doNothing().when(amInternal).updateOomAdj();
|
||||
doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());
|
||||
|
||||
@@ -892,6 +892,40 @@ public class WindowStateTests extends WindowTestsBase {
|
||||
assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdjustImeInsetsVisibilityWhenSwitchingApps() {
|
||||
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
|
||||
final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
|
||||
final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
|
||||
spyOn(imeWindow);
|
||||
doReturn(true).when(imeWindow).isVisible();
|
||||
mDisplayContent.mInputMethodWindow = imeWindow;
|
||||
|
||||
final InsetsStateController controller = mDisplayContent.getInsetsStateController();
|
||||
controller.getImeSourceProvider().setWindow(imeWindow, null, null);
|
||||
|
||||
// Simulate app requests IME with updating all windows Insets State when IME is above app.
|
||||
mDisplayContent.setImeLayeringTarget(app);
|
||||
mDisplayContent.setImeInputTarget(app);
|
||||
assertTrue(mDisplayContent.shouldImeAttachedToApp());
|
||||
controller.getImeSourceProvider().scheduleShowImePostLayout(app);
|
||||
controller.getImeSourceProvider().getSource().setVisible(true);
|
||||
controller.updateAboveInsetsState(imeWindow, false);
|
||||
|
||||
// Expect all app windows behind IME can receive IME insets visible.
|
||||
assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
|
||||
assertTrue(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
|
||||
|
||||
// Simulate app plays closing transition to app2.
|
||||
app.mActivityRecord.commitVisibility(false, false);
|
||||
assertTrue(app.mActivityRecord.mLastImeShown);
|
||||
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
|
||||
|
||||
// Verify the IME insets is visible on app, but not for app2 during app task switching.
|
||||
assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
|
||||
assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
|
||||
}
|
||||
|
||||
@UseTestDisplay(addWindows = { W_ACTIVITY })
|
||||
@Test
|
||||
public void testUpdateImeControlTargetWhenLeavingMultiWindow() {
|
||||
|
||||
Reference in New Issue
Block a user