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

Commit 02a741f7 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

A brave new world for window insets (11/n)

Implement animation listeners

Test: CTS will be added in the future
Bug: 118118435
Change-Id: If6c1153da007b80859485ecbcdc1610d1d82373e
parent 2d917da0
Loading
Loading
Loading
Loading
+21 −4
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.graphics.FrameInfo;
import android.graphics.Insets;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Build;
import android.os.Handler;
@@ -199,7 +200,7 @@ public final class Choreographer {
     * @hide
     */
    private static final String[] CALLBACK_TRACE_TITLES = {
            "input", "animation", "traversal", "commit"
            "input", "animation", "insets_animation", "traversal", "commit"
    };

    /**
@@ -209,18 +210,33 @@ public final class Choreographer {
    public static final int CALLBACK_INPUT = 0;

    /**
     * Callback type: Animation callback.  Runs before traversals.
     * Callback type: Animation callback.  Runs before {@link #CALLBACK_INSETS_ANIMATION}.
     * @hide
     */
    @TestApi
    public static final int CALLBACK_ANIMATION = 1;

    /**
     * Callback type: Animation callback to handle inset updates. This is separate from
     * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
     * {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
     * update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
     * that contains all the combined updated insets.
     * <p>
     * Both input and animation may change insets, so we need to run this after these callbacks, but
     * before traversals.
     * <p>
     * Runs before traversals.
     * @hide
     */
    public static final int CALLBACK_INSETS_ANIMATION = 2;

    /**
     * Callback type: Traversal callback.  Handles layout and draw.  Runs
     * after all other asynchronous messages have been handled.
     * @hide
     */
    public static final int CALLBACK_TRAVERSAL = 2;
    public static final int CALLBACK_TRAVERSAL = 3;

    /**
     * Callback type: Commit callback.  Handles post-draw operations for the frame.
@@ -232,7 +248,7 @@ public final class Choreographer {
     * to the view hierarchy state) actually took effect.
     * @hide
     */
    public static final int CALLBACK_COMMIT = 3;
    public static final int CALLBACK_COMMIT = 4;

    private static final int CALLBACK_LAST = CALLBACK_COMMIT;

@@ -704,6 +720,7 @@ public final class Choreographer {

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
+39 −15
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ import java.util.function.Supplier;
@VisibleForTesting
public class InsetsAnimationControlImpl implements WindowInsetsAnimationController  {

    private final Rect mTmpFrame = new Rect();

    private final WindowInsetsAnimationControlListener mListener;
    private final SparseArray<InsetsSourceConsumer> mConsumers;
    private final SparseIntArray mTypeSideMap = new SparseIntArray();
@@ -61,19 +63,23 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
    private final InsetsState mInitialInsetsState;
    private final @InsetType int mTypes;
    private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;

    private final InsetsController mController;
    private final WindowInsetsAnimationListener.InsetsAnimation mAnimation;
    private Insets mCurrentInsets;
    private Insets mPendingInsets;

    @VisibleForTesting
    public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
            InsetsState state, WindowInsetsAnimationControlListener listener,
            @InsetType int types,
            Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier) {
            Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier,
            InsetsController controller) {
        mConsumers = consumers;
        mListener = listener;
        mTypes = types;
        mTransactionApplierSupplier = transactionApplierSupplier;
        mInitialInsetsState = new InsetsState(state);
        mController = controller;
        mInitialInsetsState = new InsetsState(state, true /* copySources */);
        mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
        mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */,
                null /* typeSideMap */);
@@ -83,6 +89,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll

        // TODO: Check for controllability first and wait for IME if needed.
        listener.onReady(this, types);

        mAnimation = new WindowInsetsAnimationListener.InsetsAnimation(mTypes, mHiddenInsets,
                mShownInsets);
        mController.dispatchAnimationStarted(mAnimation);
    }

    @Override
@@ -108,29 +118,35 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll

    @Override
    public void changeInsets(Insets insets) {
        insets = sanitize(insets);
        final Insets offset = Insets.subtract(mShownInsets, insets);
        mPendingInsets = sanitize(insets);
        mController.scheduleApplyChangeInsets();
    }

    void applyChangeInsets(InsetsState state) {
        final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
        ArrayList<SurfaceParams> params = new ArrayList<>();
        if (offset.left != 0) {
            updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params);
            updateLeashesForSide(INSET_SIDE_LEFT, offset.left, params, state);
        }
        if (offset.top != 0) {
            updateLeashesForSide(INSET_SIDE_TOP, offset.top, params);
            updateLeashesForSide(INSET_SIDE_TOP, offset.top, params, state);
        }
        if (offset.right != 0) {
            updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params);
            updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, params, state);
        }
        if (offset.bottom != 0) {
            updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params);
            updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, params, state);
        }
        SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
        applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
        mCurrentInsets = insets;
        mCurrentInsets = mPendingInsets;
    }

    @Override
    public void finish(int shownTypes) {
        // TODO

        mController.dispatchAnimationFinished(mAnimation);
    }

    private Insets calculateInsets(InsetsState state, Rect frame,
@@ -146,7 +162,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
            @Nullable @InsetSide SparseIntArray typeSideMap) {
        return state.calculateInsets(frame, false /* isScreenRound */,
                false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
                .getSystemWindowInsets();
                .getInsets(mTypes);
    }

    private Insets sanitize(Insets insets) {
@@ -154,7 +170,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
    }

    private void updateLeashesForSide(@InsetSide int side, int inset,
            ArrayList<SurfaceParams> surfaceParams) {
            ArrayList<SurfaceParams> surfaceParams, InsetsState state) {
        ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
        // TODO: Implement behavior when inset spans over multiple types
        for (int i = items.size() - 1; i >= 0; i--) {
@@ -162,24 +178,32 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
            final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
            final SurfaceControl leash = consumer.getControl().getLeash();
            mTmpMatrix.setTranslate(source.getFrame().left, source.getFrame().top);
            addTranslationToMatrix(side, inset, mTmpMatrix);

            mTmpFrame.set(source.getFrame());
            addTranslationToMatrix(side, inset, mTmpMatrix, mTmpFrame);

            state.getSource(source.getType()).setFrame(mTmpFrame);
            surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f));
        }
    }

    private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m) {
    private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m, Rect frame) {
        switch (side) {
            case INSET_SIDE_LEFT:
                m.postTranslate(-inset, 0);
                frame.offset(-inset, 0);
                break;
            case INSET_SIDE_TOP:
                m.postTranslate(0, -inset);
                frame.offset(0, -inset);
                break;
            case INSET_SIDE_RIGHT:
                m.postTranslate(inset, 0);
                frame.offset(inset, 0);
                break;
            case INSET_SIDE_BOTTOM:
                m.postTranslate(0, inset);
                frame.offset(0, inset);
                break;
        }
    }
+40 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.view;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.ArraySet;
@@ -49,9 +50,29 @@ public class InsetsController implements WindowInsetsController {

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

    private boolean mAnimCallbackScheduled;

    private final Runnable mAnimCallback;

    public InsetsController(ViewRootImpl viewRoot) {
        mViewRoot = viewRoot;
        mAnimCallback = () -> {
            mAnimCallbackScheduled = false;
            if (mAnimationControls.isEmpty()) {
                return;
            }

            InsetsState state = new InsetsState(mState, true /* copySources */);
            for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
                mAnimationControls.get(i).applyChangeInsets(state);
            }
            WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
                    mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
                    null /* typeSideMap */);
            mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
        };
    }

    void onFrameChanged(Rect frame) {
@@ -82,8 +103,9 @@ public class InsetsController implements WindowInsetsController {
    @VisibleForTesting
    public WindowInsets calculateInsets(boolean isScreenRound,
            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
        mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
                null /* typeSideMap */);
        return mLastInsets;
    }

    /**
@@ -148,7 +170,7 @@ public class InsetsController implements WindowInsetsController {
        }
        final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
                mFrame, mState, listener, types,
                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView));
                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
        mAnimationControls.add(controller);
    }

@@ -200,4 +222,20 @@ public class InsetsController implements WindowInsetsController {
        pw.println(prefix); pw.println("InsetsController:");
        mState.dump(prefix + "  ", pw);
    }

    void dispatchAnimationStarted(WindowInsetsAnimationListener.InsetsAnimation animation) {
        mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation);
    }

    void dispatchAnimationFinished(WindowInsetsAnimationListener.InsetsAnimation animation) {
        mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
    }

    void scheduleApplyChangeInsets() {
        if (!mAnimCallbackScheduled) {
            mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
                    mAnimCallback, null /* token*/);
            mAnimCallbackScheduled = true;
        }
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -107,6 +107,10 @@ public class InsetsState implements Parcelable {
        set(copy);
    }

    public InsetsState(InsetsState copy, boolean copySources) {
        set(copy, copySources);
    }

    /**
     * Calculates {@link WindowInsets} based on the current source configuration.
     *
+34 −0
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.WindowInsetsAnimationListener.InsetsAnimation;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
@@ -4547,6 +4548,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        OnCapturedPointerListener mOnCapturedPointerListener;
        private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
        private WindowInsetsAnimationListener mWindowInsetsAnimationListener;
    }
    @UnsupportedAppUsage
@@ -10478,6 +10481,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
    }
    /**
     * Sets a {@link WindowInsetsAnimationListener} to be notified about animations of windows that
     * cause insets.
     *
     * @param listener The listener to set.
     * @hide pending unhide
     */
    public void setWindowInsetsAnimationListener(WindowInsetsAnimationListener listener) {
        getListenerInfo().mWindowInsetsAnimationListener = listener;
    }
    void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
            mListenerInfo.mWindowInsetsAnimationListener.onStarted(animation);
        }
    }
    WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
            return mListenerInfo.mWindowInsetsAnimationListener.onProgress(insets);
        } else {
            return insets;
        }
    }
    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
        if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) {
            mListenerInfo.mWindowInsetsAnimationListener.onFinished(animation);
        }
    }
    /**
     * Compute the view's coordinate within the surface.
     *
Loading