Merge "Better IME transition while switching app with recents (6/N)" into sc-dev am: 07d71cf8ee

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13426059

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I982540401f65128c04e4a3abd6488749b11f3c37
This commit is contained in:
Ming-Shin Lu
2021-01-29 16:51:14 +00:00
committed by Automerger Merge Worker
6 changed files with 106 additions and 8 deletions

View File

@@ -4107,8 +4107,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* Callbacks when the given type of {@link WindowContainer} animation finished running in the
* hierarchy.
*/
void onWindowAnimationFinished(int type) {
void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) {
if (type == ANIMATION_TYPE_APP_TRANSITION || type == ANIMATION_TYPE_RECENTS) {
// Unfreeze the insets state of the frozen target when the animation finished if exists.
final Task task = wc.asTask();
if (task != null) {
task.forAllWindows(w -> {
w.clearFrozenInsetsState();
}, true /* traverseTopToBottom */);
}
removeImeSurfaceImmediately();
}
}

View File

@@ -74,7 +74,7 @@ class InsetsStateController {
private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
if (w.isVisible()) {
if (w.isReadyToDispatchInsetsState()) {
w.notifyInsetsChanged();
}
};
@@ -117,7 +117,8 @@ class InsetsStateController {
final @InternalInsetsType int type = provider != null
? provider.getSource().getType() : ITYPE_INVALID;
return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
isAboveIme(target));
isAboveIme(target),
target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : mState);
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -132,7 +133,7 @@ class InsetsStateController {
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token));
return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token), mState);
}
private boolean isAboveIme(WindowContainer target) {
@@ -180,9 +181,8 @@ class InsetsStateController {
* @see #getInsetsForWindowMetrics
*/
private InsetsState getInsetsForTarget(@InternalInsetsType int type,
@WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) {
InsetsState state = mState;
@WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme,
@NonNull InsetsState state) {
if (type != ITYPE_INVALID) {
state = new InsetsState(state);
state.removeSource(type);

View File

@@ -2684,6 +2684,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
@Nullable ArrayList<WindowContainer> sources) {
final Task task = asTask();
if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
if (AppTransition.isClosingTransitOld(transit)) {
// Freezes the insets state when the window is in app exiting transition, to
// ensure the exiting window won't receive unexpected insets changes from the
// next window.
task.forAllWindows(w -> {
w.freezeInsetsState();
}, true /* traverseTopToBottom */);
}
mDisplayContent.showImeScreenshot();
}
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
@@ -2831,7 +2839,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
mSurfaceAnimationSources.clear();
if (mDisplayContent != null) {
mDisplayContent.onWindowAnimationFinished(type);
mDisplayContent.onWindowAnimationFinished(this, type);
}
}

View File

@@ -713,6 +713,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private @Nullable InsetsSourceProvider mControllableInsetProvider;
private final InsetsState mRequestedInsetsState = new InsetsState();
/**
* Freeze the insets state in some cases that not necessarily keeps up-to-date to the client.
* (e.g app exiting transition)
*/
private InsetsState mFrozenInsetsState;
@Nullable InsetsSourceProvider mPendingPositionChanged;
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
@@ -758,6 +764,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
/**
* Set a freeze state for the window to ignore dispatching its insets state to the client.
*
* Used to keep the insets state for some use cases. (e.g. app exiting transition)
*/
void freezeInsetsState() {
if (mFrozenInsetsState == null) {
mFrozenInsetsState = new InsetsState(getInsetsState(), true /* copySources */);
}
}
void clearFrozenInsetsState() {
mFrozenInsetsState = null;
}
InsetsState getFrozenInsetsState() {
return mFrozenInsetsState;
}
/**
* Check if the insets state of the window is ready to dispatch to the client when invoking
* {@link InsetsStateController#notifyInsetsChanged}.
*/
boolean isReadyToDispatchInsetsState() {
return isVisible() && mFrozenInsetsState == null;
}
void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
@Rotation int rotation, boolean requested) {
// Invisible windows and the wallpaper do not participate in the seamless rotation animation

View File

@@ -22,6 +22,8 @@ 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 android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
@@ -978,6 +980,31 @@ public class WindowContainerTests extends WindowTestsBase {
assertEquals(200, listener.mConfiguration.densityDpi);
}
@Test
public void testFreezeInsetsStateWhenAppTransition() {
final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
spyOn(win);
doReturn(true).when(task).okToAnimate();
ArrayList<WindowContainer> sources = new ArrayList<>();
sources.add(activity);
// Simulate the task applying the exit transition, verify the main window of the task
// will be set the frozen insets state.
task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
false /* isVoiceInteraction */, sources);
verify(win).freezeInsetsState();
// Simulate the task transition finished, verify the frozen insets state of the window
// will be reset.
task.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION,
task.mSurfaceAnimator.getAnimation());
verify(win).clearFrozenInsetsState();
}
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;

View File

@@ -810,4 +810,27 @@ public class WindowStateTests extends WindowTestsBase {
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
}
@Test
public void testSetFreezeInsetsState() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
spyOn(app);
doReturn(true).when(app).isVisible();
// Set freezing the insets state to make the window ignore to dispatch insets changed.
final InsetsState expectedState = new InsetsState(app.getInsetsState(),
true /* copySources */);
app.freezeInsetsState();
assertEquals(expectedState, app.getFrozenInsetsState());
assertFalse(app.isReadyToDispatchInsetsState());
assertEquals(expectedState, app.getInsetsState());
mDisplayContent.getInsetsStateController().notifyInsetsChanged();
verify(app, never()).notifyInsetsChanged();
// Unfreeze the insets state to make the window can dispatch insets changed.
app.clearFrozenInsetsState();
assertTrue(app.isReadyToDispatchInsetsState());
mDisplayContent.getInsetsStateController().notifyInsetsChanged();
verify(app).notifyInsetsChanged();
}
}