Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f536fa9 authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Better IME transition while switching app with recents (6/N)

Add WindowState#{freezeInsetsState, clearFrozenInsetsState} to freeze
the insets state of the window to keep 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.

And, in order to easy maintain the logic of dispatching all windows's
insets changed event in InsetsStateController, instead of adding
complicated rule in InsetsStateController to judge if the window can
disatch insets change, we can replace with
WindowState#isReadyToDispatchInsetsState() to wrap the dispatch
judgement.

Bug: 166736352
Test: atest CtsWindowManagerDeviceTestCases WmTests
Test: atest WindowStateTests#testSetFreezeInsetsState
Test: atest WindowContainerTests#testFreezeInsetsStateWhenAppTransition

Change-Id: I56418733c1abbd73e88a8918a5d55ecc15344c5e
parent cd6911ec
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -4102,8 +4102,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();
        }
    }
+6 −6
Original line number Diff line number Diff line
@@ -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);
+9 −1
Original line number Diff line number Diff line
@@ -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);
        }
    }

+33 −0
Original line number Diff line number Diff line
@@ -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
+27 −0
Original line number Diff line number Diff line
@@ -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;
Loading