Merge "Improve InsetsPolicy#adjustVisibilityForIme when switching apps" into sc-v2-dev

This commit is contained in:
TreeHugger Robot
2021-08-23 16:02:14 +00:00
committed by Android (Google) Code Review
6 changed files with 160 additions and 10 deletions

View File

@@ -675,6 +675,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Whether the IME is showing when transitioning away from this activity. */ /** Whether the IME is showing when transitioning away from this activity. */
boolean mLastImeShown; 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 * 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 * 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(); associateStartingDataWithTask();
overrideConfigurationPropagation(mStartingWindow, task); overrideConfigurationPropagation(mStartingWindow, task);
} }
mImeInsetsFrozenUntilStartInput = false;
} }
if (rootTask != null && rootTask.topRunningActivity() == this) { if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -4938,6 +4945,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
&& imeInputTarget.getWindow().mActivityRecord == this && imeInputTarget.getWindow().mActivityRecord == this
&& mDisplayContent.mInputMethodWindow != null && mDisplayContent.mInputMethodWindow != null
&& mDisplayContent.mInputMethodWindow.isVisible(); && mDisplayContent.mInputMethodWindow.isVisible();
mImeInsetsFrozenUntilStartInput = true;
} }
final DisplayContent displayContent = getDisplayContent(); 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 // 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). // next activity won't report idle (e.g. repeated view animation).
mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); 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. */ /** Returns true if the configuration is compatible with this activity. */
boolean isConfigurationCompatible(Configuration config) { boolean isConfigurationCompatible(Configuration config) {
final int orientation = getRequestedOrientation(); final int orientation = getRequestedOrientation();

View File

@@ -4022,6 +4022,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void updateImeInputAndControlTarget(WindowState target) { void updateImeInputAndControlTarget(WindowState target) {
if (mImeInputTarget != target) { if (mImeInputTarget != target) {
ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target); ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
if (target != null && target.mActivityRecord != null) {
target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
}
setImeInputTarget(target); setImeInputTarget(target);
updateImeControlTarget(); updateImeControlTarget();
} }

View File

@@ -213,7 +213,7 @@ class InsetsPolicy {
InsetsState getInsetsForWindow(WindowState target) { InsetsState getInsetsForWindow(WindowState target) {
final InsetsState originalState = mStateController.getInsetsForWindow(target); final InsetsState originalState = mStateController.getInsetsForWindow(target);
final InsetsState state = adjustVisibilityForTransientTypes(originalState); 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; return state;
} }
// Navigation bar insets is always visible to IME. private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
private static InsetsState adjustVisibilityForIme(InsetsState originalState,
boolean copyState) { boolean copyState) {
final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); if (w.mIsImWindow) {
if (originalNavSource != null && !originalNavSource.isVisible()) { // Navigation bar insets is always visible to IME.
final InsetsState state = copyState ? new InsetsState(originalState) : originalState; final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
final InsetsSource navSource = new InsetsSource(originalNavSource); if (originalNavSource != null && !originalNavSource.isVisible()) {
navSource.setVisible(true); final InsetsState state = copyState ? new InsetsState(originalState)
state.addSource(navSource); : originalState;
return state; 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; return originalState;
} }

View File

@@ -40,6 +40,7 @@ import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_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_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -2907,6 +2908,73 @@ public class ActivityRecordTests extends WindowTestsBase {
assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); 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) { private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.mStartingSurface); assertNotNull(atoken.mStartingSurface);
assertNotNull(atoken.mStartingData); assertNotNull(atoken.mStartingData);

View File

@@ -258,6 +258,7 @@ public class SystemServicesTestRule implements TestRule {
final ActivityManagerInternal amInternal = mAmService.mInternal; final ActivityManagerInternal amInternal = mAmService.mInternal;
spyOn(amInternal); spyOn(amInternal);
doNothing().when(amInternal).trimApplications(); doNothing().when(amInternal).trimApplications();
doNothing().when(amInternal).scheduleAppGcs();
doNothing().when(amInternal).updateCpuStats(); doNothing().when(amInternal).updateCpuStats();
doNothing().when(amInternal).updateOomAdj(); doNothing().when(amInternal).updateOomAdj();
doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean()); doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());

View File

@@ -892,6 +892,40 @@ public class WindowStateTests extends WindowTestsBase {
assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); 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 }) @UseTestDisplay(addWindows = { W_ACTIVITY })
@Test @Test
public void testUpdateImeControlTargetWhenLeavingMultiWindow() { public void testUpdateImeControlTargetWhenLeavingMultiWindow() {