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

Commit 2ee1e93e authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[OriginTransition] Make the library resilient to view being detached in...

Merge "[OriginTransition] Make the library resilient to view being detached in the middle of transition." into main
parents 356f6e72 f59a78dd
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -107,7 +107,7 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
            IBinder mergeTarget,
            IRemoteTransitionFinishedCallback finishCallback) {
        logD("mergeAnimation - " + info);
        mHandler.post(this::cancel);
        cancel();
    }

    @Override
@@ -129,7 +129,7 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
    @Override
    public void onTransitionConsumed(IBinder transition, boolean aborted) {
        logD("onTransitionConsumed - aborted: " + aborted);
        mHandler.post(this::cancel);
        cancel();
    }

    private void startAnimationInternal(
@@ -342,11 +342,14 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
        mFinishCallback = null;
    }

    private void cancel() {
    public void cancel() {
        logD("cancel()");
        mHandler.post(
                () -> {
                    if (mAnimator != null) {
                        mAnimator.cancel();
                    }
                });
    }

    private static void logD(String msg) {
+8 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import java.util.function.Supplier;
/**
 * A session object that holds origin transition states for starting an activity from an on-screen
 * UI component and smoothly transitioning back from the activity to the same UI component.
 *
 * @hide
 */
public class OriginTransitionSession {
@@ -143,6 +144,12 @@ public class OriginTransitionSession {
                logE("Unable to cancel origin transition!", e);
            }
        }
        if (mEntryTransition instanceof OriginRemoteTransition) {
            ((OriginRemoteTransition) mEntryTransition).cancel();
        }
        if (mExitTransition instanceof OriginRemoteTransition) {
            ((OriginRemoteTransition) mExitTransition).cancel();
        }
    }

    private boolean hasEntryTransition() {
@@ -182,6 +189,7 @@ public class OriginTransitionSession {

    /**
     * A builder to build a {@link OriginTransitionSession}.
     *
     * @hide
     */
    public static class Builder {
+75 −13
Original line number Diff line number Diff line
@@ -24,13 +24,15 @@ import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.ViewTreeObserver;

import java.util.ArrayList;
import java.util.List;
@@ -51,8 +53,9 @@ public class ViewUIComponent implements UIComponent {
    private final Path mClippingPath = new Path();
    private final Outline mClippingOutline = new Outline();

    private final OnDrawListener mOnDrawListener = this::postDraw;
    private final LifecycleListener mLifecycleListener = new LifecycleListener();
    private final View mView;
    private final Handler mMainHandler;

    @Nullable private SurfaceControl mSurfaceControl;
    @Nullable private Surface mSurface;
@@ -62,6 +65,7 @@ public class ViewUIComponent implements UIComponent {

    public ViewUIComponent(View view) {
        mView = view;
        mMainHandler = new Handler(Looper.getMainLooper());
    }

    /**
@@ -110,11 +114,11 @@ public class ViewUIComponent implements UIComponent {
        t.reparent(mSurfaceControl, transitionLeash).show(mSurfaceControl);

        // Make sure view draw triggers surface draw.
        mView.getViewTreeObserver().addOnDrawListener(mOnDrawListener);
        mLifecycleListener.register();

        // Make the view invisible AFTER the surface is shown.
        t.addTransactionCommittedListener(
                        mView::post,
                        this::post,
                        () -> {
                            logD("Surface attached!");
                            forceDraw();
@@ -129,14 +133,14 @@ public class ViewUIComponent implements UIComponent {
        SurfaceControl sc = mSurfaceControl;
        mSurface = null;
        mSurfaceControl = null;
        mView.getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
        mLifecycleListener.unregister();
        // Restore view visibility
        mView.setVisibility(mVisibleOverride ? View.VISIBLE : View.INVISIBLE);
        // Clean up surfaces.
        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
        t.reparent(sc, null)
                .addTransactionCommittedListener(
                        mView::post,
                        this::post,
                        () -> {
                            s.release();
                            sc.release();
@@ -269,7 +273,66 @@ public class ViewUIComponent implements UIComponent {
            return;
        }
        mDirty = true;
        mView.post(this::draw);
        post(this::draw);
    }

    private void post(Runnable r) {
        if (mView.isAttachedToWindow()) {
            mView.post(r);
        } else {
            // If the view is detached from window, {@code View.post()} will postpone the action
            // until the view is attached again. However, we don't know if the view will be attached
            // again, so we post the action to the main thread in this case. This could lead to race
            // condition if the attachment change caused a thread switching, and it's the caller's
            // responsibility to ensure the window attachment state doesn't change unexpectedly.
            if (DEBUG) {
                Log.w(TAG, mView + " is not attached. Posting action to main thread!");
            }
            mMainHandler.post(r);
        }
    }

    /** A listener for monitoring view life cycles. */
    private class LifecycleListener
            implements ViewTreeObserver.OnDrawListener, View.OnAttachStateChangeListener {
        private boolean mRegistered;

        @Override
        public void onDraw() {
            // View draw should trigger surface draw.
            postDraw();
        }

        @Override
        public void onViewAttachedToWindow(View v) {
            // empty
        }

        @Override
        public void onViewDetachedFromWindow(View v) {
            Log.w(
                    TAG,
                    v + " is detached from the window. Unregistering the life cycle listener ...");
            unregister();
        }

        public void register() {
            if (mRegistered) {
                return;
            }
            mRegistered = true;
            mView.getViewTreeObserver().addOnDrawListener(this);
            mView.addOnAttachStateChangeListener(this);
        }

        public void unregister() {
            if (!mRegistered) {
                return;
            }
            mRegistered = false;
            mView.getViewTreeObserver().removeOnDrawListener(this);
            mView.removeOnAttachStateChangeListener(this);
        }
    }

    /** @hide */
@@ -278,34 +341,33 @@ public class ViewUIComponent implements UIComponent {

        @Override
        public Transaction setAlpha(ViewUIComponent ui, float alpha) {
            mChanges.add(() -> ui.mView.post(() -> ui.setAlpha(alpha)));
            mChanges.add(() -> ui.post(() -> ui.setAlpha(alpha)));
            return this;
        }

        @Override
        public Transaction setVisible(ViewUIComponent ui, boolean visible) {
            mChanges.add(() -> ui.mView.post(() -> ui.setVisible(visible)));
            mChanges.add(() -> ui.post(() -> ui.setVisible(visible)));
            return this;
        }

        @Override
        public Transaction setBounds(ViewUIComponent ui, Rect bounds) {
            mChanges.add(() -> ui.mView.post(() -> ui.setBounds(bounds)));
            mChanges.add(() -> ui.post(() -> ui.setBounds(bounds)));
            return this;
        }

        @Override
        public Transaction attachToTransitionLeash(
                ViewUIComponent ui, SurfaceControl transitionLeash, int w, int h) {
            mChanges.add(
                    () -> ui.mView.post(() -> ui.attachToTransitionLeash(transitionLeash, w, h)));
            mChanges.add(() -> ui.post(() -> ui.attachToTransitionLeash(transitionLeash, w, h)));
            return this;
        }

        @Override
        public Transaction detachFromTransitionLeash(
                ViewUIComponent ui, Executor executor, Runnable onDone) {
            mChanges.add(() -> ui.mView.post(() -> ui.detachFromTransitionLeash(executor, onDone)));
            mChanges.add(() -> ui.post(() -> ui.detachFromTransitionLeash(executor, onDone)));
            return this;
        }