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

Commit 514f6576 authored by Tiger's avatar Tiger Committed by Tiger Huang
Browse files

Only play insets resize animation when needed

There are two cases that we might mistrigger the insets resize
animation which would cause the window to delay updating the insets
until the animation finishes:

1. The insets source itself won't animate when resizing. For example,
   status bar might resize itself when the state of containing the
   display cutout is changed. Status bar will resize to the target size
   in just one frame, so we shouldn't trigger the fake insets resize
   animation (duration: 300 ms) and report the fake progress to the app.

2. When the insets source is invisible at the server side.
   InsetsSourceProvider would reset the source frame when mServerVisible
   is false. Although the insets source frame size is changed, but there
   is no resize animation in this case.

This CL introduces an InsetsSource flag, FLAG_ANIMATE_RESIZING. Only the
source window which plays resize animation should apply it.

Bug: 291562764
Test: atest InsetsControllerTest
Test: Fold and unfold a foldable device and see if there is an
      InsetsResizeAnimationRunner.
Merged-In: I484d9da89ca8e21578f2005d6409c4e8a919ae37
Change-Id: I484d9da89ca8e21578f2005d6409c4e8a919ae37
parent ee908655
Loading
Loading
Loading
Loading
+11 −7
Original line number Diff line number Diff line
@@ -737,15 +737,17 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation

                @Override
                public void onIdMatch(InsetsSource source1, InsetsSource source2) {
                    final @InsetsType int type = source1.getType();
                    if ((type & Type.systemBars()) == 0
                    final Rect frame1 = source1.getFrame();
                    final Rect frame2 = source2.getFrame();
                    if (!source1.hasFlags(InsetsSource.FLAG_ANIMATE_RESIZING)
                            || !source2.hasFlags(InsetsSource.FLAG_ANIMATE_RESIZING)
                            || !source1.isVisible() || !source2.isVisible()
                            || source1.getFrame().equals(source2.getFrame())
                            || frame1.equals(frame2) || frame1.isEmpty() || frame2.isEmpty()
                            || !(Rect.intersects(mFrame, source1.getFrame())
                                    || Rect.intersects(mFrame, source2.getFrame()))) {
                        return;
                    }
                    mTypes |= type;
                    mTypes |= source1.getType();
                    if (mToState == null) {
                        mToState = new InsetsState();
                    }
@@ -877,7 +879,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
            return false;
        }
        if (DEBUG) Log.d(TAG, "onStateChanged: " + state);
        mLastDispatchedState.set(state, true /* copySources */);

        final InsetsState lastState = new InsetsState(mState, true /* copySources */);
        updateState(state);
@@ -888,10 +889,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
                true /* excludesInvisibleIme */)) {
            if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
            mHost.notifyInsetsChanged();
            if (lastState.getDisplayFrame().equals(mState.getDisplayFrame())) {
                InsetsState.traverse(lastState, mState, mStartResizingAnimationIfNeeded);
            if (mLastDispatchedState.getDisplayFrame().equals(state.getDisplayFrame())) {
                // Here compares the raw states instead of the overridden ones because we don't want
                // to animate an insets source that its mServerVisible is false.
                InsetsState.traverse(mLastDispatchedState, state, mStartResizingAnimationIfNeeded);
            }
        }
        mLastDispatchedState.set(state, true /* copySources */);
        return true;
    }

+9 −0
Original line number Diff line number Diff line
@@ -99,11 +99,17 @@ public class InsetsSource implements Parcelable {
     */
    public static final int FLAG_FORCE_CONSUMING = 1 << 2;

    /**
     * Controls whether the insets source will play an animation when resizing.
     */
    public static final int FLAG_ANIMATE_RESIZING = 1 << 3;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = "FLAG_", value = {
            FLAG_SUPPRESS_SCRIM,
            FLAG_INSETS_ROUNDED_CORNER,
            FLAG_FORCE_CONSUMING,
            FLAG_ANIMATE_RESIZING,
    })
    public @interface Flags {}

@@ -540,6 +546,9 @@ public class InsetsSource implements Parcelable {
        if ((flags & FLAG_FORCE_CONSUMING) != 0) {
            joiner.add("FORCE_CONSUMING");
        }
        if ((flags & FLAG_ANIMATE_RESIZING) != 0) {
            joiner.add("ANIMATE_RESIZING");
        }
        return joiner.toString();
    }

+63 −19
Original line number Diff line number Diff line
@@ -21,12 +21,11 @@ import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsSource.FLAG_ANIMATE_RESIZING;
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED;
import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.defaultVisible;
@@ -671,36 +670,81 @@ public class InsetsControllerTest {
    }

    @Test
    public void testResizeAnimation_insetsTypes() {
        for (int i = 0; i < SIZE; i++) {
            final @InsetsType int type = 1 << i;
            final @AnimationType int expectedAnimationType = (type & systemBars()) != 0
                            ? ANIMATION_TYPE_RESIZE
                            : ANIMATION_TYPE_NONE;
            doTestResizeAnimation_insetsTypes(type, expectedAnimationType);
        }
    public void testResizeAnimation_withFlagAnimateResizing() {
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            final int id = ID_NAVIGATION_BAR;
            final @InsetsType int type = navigationBars();
            final InsetsState state1 = new InsetsState();
            state1.getOrCreateSource(id, type)
                    .setVisible(true)
                    .setFrame(0, 0, 500, 50)
                    .setFlags(FLAG_ANIMATE_RESIZING, FLAG_ANIMATE_RESIZING);
            final InsetsState state2 = new InsetsState(state1, true /* copySources */);
            state2.peekSource(id).setFrame(0, 0, 500, 60);

            // New insets source won't cause the resize animation.
            mController.onStateChanged(state1);
            assertEquals("There must not be resize animation.", ANIMATION_TYPE_NONE,
                    mController.getAnimationType(type));

            // Changing frame of the source with FLAG_ANIMATE_RESIZING will cause the resize
            // animation.
            mController.onStateChanged(state2);
            assertEquals("There must be resize animation.", ANIMATION_TYPE_RESIZE,
                    mController.getAnimationType(type));
        });
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    }

    private void doTestResizeAnimation_insetsTypes(@InsetsType int type,
            @AnimationType int expectedAnimationType) {
        final int id = type;
    @Test
    public void testResizeAnimation_withoutFlagAnimateResizing() {
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            final int id = ID_STATUS_BAR;
            final @InsetsType int type = statusBars();
            final InsetsState state1 = new InsetsState();
            state1.getOrCreateSource(id, type).setVisible(true).setFrame(0, 0, 500, 50);
            state1.getOrCreateSource(id, type)
                    .setVisible(true)
                    .setFrame(0, 0, 500, 50)
                    .setFlags(0, FLAG_ANIMATE_RESIZING);
            final InsetsState state2 = new InsetsState(state1, true /* copySources */);
            state2.peekSource(id).setFrame(0, 0, 500, 60);
            final String message = "Animation type of " + WindowInsets.Type.toString(type) + ":";
            final String message = "There must not be resize animation.";

            // New insets source won't cause the resize animation.
            mController.onStateChanged(state1);
            assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));

            // Changing frame of the source without FLAG_ANIMATE_RESIZING must not cause the resize
            // animation.
            mController.onStateChanged(state2);
            assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
        });
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    }

    @Test
    public void testResizeAnimation_sourceFrame() {
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            final int id = ID_STATUS_BAR;
            final @InsetsType int type = statusBars();
            final InsetsState state1 = new InsetsState();
            state1.setDisplayFrame(new Rect(0, 0, 500, 1000));
            state1.getOrCreateSource(id, type).setFrame(0, 0, 500, 50);
            final InsetsState state2 = new InsetsState(state1, true /* copySources */);
            state2.setDisplayFrame(state1.getDisplayFrame());
            state2.peekSource(id).setFrame(0, 0, 500, 0);
            final String message = "There must not be resize animation.";

            // New insets source won't cause the resize animation.
            mController.onStateChanged(state1);
            assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));

            // Changing frame might cause the resize animation. This depends on the insets type.
            // Changing frame won't cause the resize animation if the new frame is empty.
            mController.onStateChanged(state2);
            assertEquals(message, expectedAnimationType, mController.getAnimationType(type));
            assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));

            // Cancel the existing animations for the next iteration.
            mController.cancelExistingAnimations();
            // Changing frame won't cause the resize animation if the existing frame is empty.
            mController.onStateChanged(state1);
            assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
        });
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();