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

Commit 8a4532cf authored by Jorim Jaggi's avatar Jorim Jaggi Committed by Android (Google) Code Review
Browse files

Merge "Implement proper lifecycle for WindowInsetsAnimationController"

parents 2d16430d 5ed50cc1
Loading
Loading
Loading
Loading
+51 −4
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.view.InsetsState.INSET_SIDE_BOTTOM;
import static android.view.InsetsState.INSET_SIDE_LEFT;
import static android.view.InsetsState.INSET_SIDE_RIGHT;
import static android.view.InsetsState.INSET_SIDE_TOP;
import static android.view.InsetsState.toPublicType;

import android.annotation.Nullable;
import android.graphics.Insets;
@@ -33,6 +34,7 @@ import android.util.SparseSetArray;
import android.view.InsetsState.InsetSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetType;
import android.view.WindowInsetsAnimationListener.InsetsAnimation;
import android.view.WindowManager.LayoutParams;

import com.android.internal.annotations.VisibleForTesting;
@@ -66,8 +68,12 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
    private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
    private final InsetsController mController;
    private final WindowInsetsAnimationListener.InsetsAnimation mAnimation;
    private final Rect mFrame;
    private Insets mCurrentInsets;
    private Insets mPendingInsets;
    private boolean mFinished;
    private boolean mCancelled;
    private int mFinishedShownTypes;

    @VisibleForTesting
    public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
@@ -86,6 +92,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
                null /* typeSideMap */);
        mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */,
                mTypeSideMap);
        mFrame = new Rect(frame);
        buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers);

        // TODO: Check for controllability first and wait for IME if needed.
@@ -119,12 +126,26 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll

    @Override
    public void changeInsets(Insets insets) {
        if (mFinished) {
            throw new IllegalStateException(
                    "Can't change insets on an animation that is finished.");
        }
        if (mCancelled) {
            throw new IllegalStateException(
                    "Can't change insets on an animation that is cancelled.");
        }
        mPendingInsets = sanitize(insets);
        mController.scheduleApplyChangeInsets();
    }

    @VisibleForTesting
    public void applyChangeInsets(InsetsState state) {
    /**
     * @return Whether the finish callback of this animation should be invoked.
     */
    public boolean applyChangeInsets(InsetsState state) {
        if (mCancelled) {
            return false;
        }
        final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
        ArrayList<SurfaceParams> params = new ArrayList<>();
        if (offset.left != 0) {
@@ -144,13 +165,40 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
        SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
        applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
        mCurrentInsets = mPendingInsets;
        if (mFinished) {
            mController.notifyFinished(this, mFinishedShownTypes);
        }
        return mFinished;
    }

    @Override
    public void finish(int shownTypes) {
        // TODO
        if (mCancelled) {
            return;
        }
        InsetsState state = new InsetsState(mController.getState());
        for (int i = mConsumers.size() - 1; i >= 0; i--) {
            InsetsSourceConsumer consumer = mConsumers.valueAt(i);
            boolean visible = (shownTypes & toPublicType(consumer.getType())) != 0;
            state.getSource(consumer.getType()).setVisible(visible);
        }
        Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */);
        changeInsets(insets);
        mFinished = true;
        mFinishedShownTypes = shownTypes;
    }

        mController.dispatchAnimationFinished(mAnimation);
    @VisibleForTesting
    public void onCancelled() {
        if (mFinished) {
            return;
        }
        mCancelled = true;
        mListener.onCancelled();
    }

    InsetsAnimation getAnimation() {
        return mAnimation;
    }

    private Insets calculateInsets(InsetsState state, Rect frame,
@@ -225,4 +273,3 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
        }
    }
}
+58 −17
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package android.view;

import static android.view.InsetsState.TYPE_IME;
import static android.view.InsetsState.toPublicType;
import static android.view.WindowInsets.Type.all;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -99,6 +101,7 @@ public class InsetsController implements WindowInsetsController {

    private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
    private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
    private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
    private WindowInsets mLastInsets;

    private boolean mAnimCallbackScheduled;
@@ -107,7 +110,6 @@ public class InsetsController implements WindowInsetsController {

    private final Rect mLastLegacyContentInsets = new Rect();
    private final Rect mLastLegacyStableInsets = new Rect();
    private ObjectAnimator mAnimator;
    private @AnimationDirection int mAnimationDirection;

    private int mPendingTypesToShow;
@@ -122,19 +124,29 @@ public class InsetsController implements WindowInsetsController {
                return;
            }

            mTmpFinishedControls.clear();
            InsetsState state = new InsetsState(mState, true /* copySources */);
            for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
                mAnimationControls.get(i).applyChangeInsets(state);
                InsetsAnimationControlImpl control = mAnimationControls.get(i);
                if (mAnimationControls.get(i).applyChangeInsets(state)) {
                    mTmpFinishedControls.add(control);
                }
            }

            WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
                    mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
                    mLastLegacyContentInsets, mLastLegacyStableInsets, mLastLegacySoftInputMode,
                    null /* typeSideMap */);
            mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);

            for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) {
                dispatchAnimationFinished(mTmpFinishedControls.get(i).getAnimation());
            }
        };
    }

    void onFrameChanged(Rect frame) {
    @VisibleForTesting
    public void onFrameChanged(Rect frame) {
        if (mFrame.equals(frame)) {
            return;
        }
@@ -279,7 +291,8 @@ public class InsetsController implements WindowInsetsController {
            // nothing to animate.
            return;
        }
        // TODO: Check whether we already have a controller.
        cancelExistingControllers(types);

        final ArraySet<Integer> internalTypes = mState.toInternalType(types);
        final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();

@@ -321,7 +334,7 @@ public class InsetsController implements WindowInsetsController {
                    // Show request
                    switch(consumer.requestShow(fromIme)) {
                        case ShowResult.SHOW_IMMEDIATELY:
                            typesReady |= InsetsState.toPublicType(TYPE_IME);
                            typesReady |= InsetsState.toPublicType(consumer.getType());
                            break;
                        case ShowResult.SHOW_DELAYED:
                            isReady = false;
@@ -365,6 +378,36 @@ public class InsetsController implements WindowInsetsController {
        return typesReady;
    }

    private void cancelExistingControllers(@InsetType int types) {
        for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
            InsetsAnimationControlImpl control = mAnimationControls.get(i);
            if ((control.getTypes() & types) != 0) {
                cancelAnimation(control);
            }
        }
    }

    @VisibleForTesting
    public void notifyFinished(InsetsAnimationControlImpl controller, int shownTypes) {
        mAnimationControls.remove(controller);
        hideDirectly(controller.getTypes() & ~shownTypes);
        showDirectly(controller.getTypes() & shownTypes);
    }

    void notifyControlRevoked(InsetsSourceConsumer consumer) {
        for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
            InsetsAnimationControlImpl control = mAnimationControls.get(i);
            if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
                cancelAnimation(control);
            }
        }
    }

    private void cancelAnimation(InsetsAnimationControlImpl control) {
        control.onCancelled();
        mAnimationControls.remove(control);
    }

    private void applyLocalVisibilityOverride() {
        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
            final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i);
@@ -455,8 +498,13 @@ public class InsetsController implements WindowInsetsController {
        }

        WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {

            private WindowInsetsAnimationController mController;
            private ObjectAnimator mAnimator;

            @Override
            public void onReady(WindowInsetsAnimationController controller, int types) {
                mController = controller;
                if (show) {
                    showDirectly(types);
                } else {
@@ -474,10 +522,6 @@ public class InsetsController implements WindowInsetsController {
                        : ANIMATION_DURATION_HIDE_MS);
                mAnimator.setInterpolator(INTERPOLATOR);
                mAnimator.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationCancel(Animator animation) {
                        onAnimationFinish();
                    }

                    @Override
                    public void onAnimationEnd(Animator animation) {
@@ -488,15 +532,15 @@ public class InsetsController implements WindowInsetsController {
            }

            @Override
            public void onCancelled() {}
            public void onCancelled() {
                mAnimator.cancel();
            }

            private void onAnimationFinish() {
                mAnimationDirection = DIRECTION_NONE;
                mController.finish(show ? types : 0);
            }
        };
        // TODO: Instead of clearing this here, properly wire up
        // InsetsAnimationControlImpl.finish() to remove this from mAnimationControls.
        mAnimationControls.clear();

        // Show/hide animations always need to be relative to the display frame, in order that shown
        // and hidden state insets are correct.
@@ -522,10 +566,7 @@ public class InsetsController implements WindowInsetsController {
     */
    @VisibleForTesting
    public void cancelExistingAnimation() {
        mAnimationDirection = DIRECTION_NONE;
        if (mAnimator != null) {
            mAnimator.cancel();
        }
        cancelExistingControllers(all());
    }

    void dump(String prefix, PrintWriter pw) {
+3 −0
Original line number Diff line number Diff line
@@ -77,6 +77,9 @@ public class InsetsSourceConsumer {
        if (applyLocalVisibilityOverride()) {
            mController.notifyVisibilityChanged();
        }
        if (mSourceControl == null) {
            mController.notifyControlRevoked(this);
        }
    }

    @VisibleForTesting
+1 −1
Original line number Diff line number Diff line
@@ -10968,7 +10968,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
        if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) {
        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
            mListenerInfo.mWindowInsetsAnimationListener.onFinished(animation);
        }
    }
+23 −0
Original line number Diff line number Diff line
@@ -129,6 +129,29 @@ public class InsetsAnimationControlImplTest {
        assertPosition(navParams.matrix, new Rect(400, 0, 500, 500), new Rect(460, 0, 560, 500));
    }

    @Test
    public void testFinishing() {
        when(mMockController.getState()).thenReturn(mInsetsState);
        mController.finish(sideBars());
        mController.applyChangeInsets(mInsetsState);
        assertFalse(mInsetsState.getSource(TYPE_TOP_BAR).isVisible());
        assertTrue(mInsetsState.getSource(TYPE_NAVIGATION_BAR).isVisible());
        assertEquals(Insets.of(0, 0, 100, 0), mController.getCurrentInsets());
        verify(mMockController).notifyFinished(eq(mController), eq(sideBars()));
    }

    @Test
    public void testCancelled() {
        mController.onCancelled();
        try {
            mController.changeInsets(Insets.NONE);
            fail("Expected exception to be thrown");
        } catch (IllegalStateException ignored) {
        }
        verify(mMockListener).onCancelled();
        mController.finish(sideBars());
    }

    private void assertPosition(Matrix m, Rect original, Rect transformed) {
        RectF rect = new RectF(original);
        rect.offsetTo(0, 0);
Loading