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

Commit f754d5df authored by Wei Sheng Shih's avatar Wei Sheng Shih Committed by Android (Google) Code Review
Browse files

Merge "Fixes the flicker when transfer splash screen view to client" into sc-v2-dev

parents 837add11 379994b4
Loading
Loading
Loading
Loading
+0 −12
Original line number Diff line number Diff line
@@ -140,7 +140,6 @@ import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
import android.window.SplashScreen;
import android.window.SplashScreenView;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -969,7 +968,6 @@ public class Activity extends ContextThemeWrapper
    private UiTranslationController mUiTranslationController;

    private SplashScreen mSplashScreen;
    private SplashScreenView mSplashScreenView;

    private final WindowControllerCallback mWindowControllerCallback =
            new WindowControllerCallback() {
@@ -1640,16 +1638,6 @@ public class Activity extends ContextThemeWrapper
        }
    }

    /** @hide */
    public void setSplashScreenView(SplashScreenView v) {
        mSplashScreenView = v;
    }

    /** @hide */
    SplashScreenView getSplashScreenView() {
        return mSplashScreenView;
    }

    /**
     * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
     * the attribute {@link android.R.attr#persistableMode} set to
+37 −47
Original line number Diff line number Diff line
@@ -166,6 +166,7 @@ import android.view.Choreographer;
import android.view.Display;
import android.view.DisplayAdjustments;
import android.view.DisplayAdjustments.FixedRotationAdjustments;
import android.view.SurfaceControl;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewDebug;
@@ -235,7 +236,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

/**
@@ -4074,10 +4074,11 @@ public final class ActivityThread extends ClientTransactionHandler

    @Override
    public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
            @Nullable SplashScreenView.SplashScreenViewParcelable parcelable) {
            @Nullable SplashScreenView.SplashScreenViewParcelable parcelable,
            @NonNull SurfaceControl startingWindowLeash) {
        final DecorView decorView = (DecorView) r.window.peekDecorView();
        if (parcelable != null && decorView != null) {
            createSplashScreen(r, decorView, parcelable);
            createSplashScreen(r, decorView, parcelable, startingWindowLeash);
        } else {
            // shouldn't happen!
            Slog.e(TAG, "handleAttachSplashScreenView failed, unable to attach");
@@ -4085,61 +4086,50 @@ public final class ActivityThread extends ClientTransactionHandler
    }

    private void createSplashScreen(ActivityClientRecord r, DecorView decorView,
            SplashScreenView.SplashScreenViewParcelable parcelable) {
            SplashScreenView.SplashScreenViewParcelable parcelable,
            @NonNull SurfaceControl startingWindowLeash) {
        final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity);
        final SplashScreenView view = builder.createFromParcel(parcelable).build();
        decorView.addView(view);
        view.attachHostActivityAndSetSystemUIColors(r.activity, r.window);
        view.requestLayout();
        // Ensure splash screen view is shown before remove the splash screen window.
        final ViewRootImpl impl = decorView.getViewRootImpl();
        final boolean hardwareEnabled = impl != null && impl.isHardwareEnabled();
        final AtomicBoolean notified = new AtomicBoolean();
        if (hardwareEnabled) {
            final Runnable frameCommit = new Runnable() {
                        @Override
                        public void run() {
                            view.post(() -> {
                                if (!notified.get()) {
                                    view.getViewTreeObserver().unregisterFrameCommitCallback(this);
                                    ActivityClient.getInstance().reportSplashScreenAttached(
                                            r.token);
                                    notified.set(true);
                                }
                            });
                        }
                    };
            view.getViewTreeObserver().registerFrameCommitCallback(frameCommit);
        } else {
            final ViewTreeObserver.OnDrawListener onDrawListener =
                    new ViewTreeObserver.OnDrawListener() {

        view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
            @Override
            public void onDraw() {
                            view.post(() -> {
                                if (!notified.get()) {
                                    view.getViewTreeObserver().removeOnDrawListener(this);
                                    ActivityClient.getInstance().reportSplashScreenAttached(
                                            r.token);
                                    notified.set(true);
                // Transfer the splash screen view from shell to client.
                // Call syncTransferSplashscreenViewTransaction at the first onDraw so we can ensure
                // the client view is ready to show and we can use applyTransactionOnDraw to make
                // all transitions happen at the same frame.
                syncTransferSplashscreenViewTransaction(
                        view, r.token, decorView, startingWindowLeash);
                view.postOnAnimation(() -> view.getViewTreeObserver().removeOnDrawListener(this));
            }
        });
    }
                    };
            view.getViewTreeObserver().addOnDrawListener(onDrawListener);
        }
    }

    @Override
    public void handOverSplashScreenView(@NonNull ActivityClientRecord r) {
        final SplashScreenView v = r.activity.getSplashScreenView();
        if (v == null) {
            return;
        }
    private void reportSplashscreenViewShown(IBinder token, SplashScreenView view) {
        ActivityClient.getInstance().reportSplashScreenAttached(token);
        synchronized (this) {
            if (mSplashScreenGlobal != null) {
                mSplashScreenGlobal.handOverSplashScreenView(r.token, v);
                mSplashScreenGlobal.handOverSplashScreenView(token, view);
            }
        }
    }

    private void syncTransferSplashscreenViewTransaction(SplashScreenView view, IBinder token,
            View decorView, @NonNull SurfaceControl startingWindowLeash) {
        // Ensure splash screen view is shown before remove the splash screen window.
        // Once the copied splash screen view is onDrawn on decor view, use applyTransactionOnDraw
        // to ensure the transfer of surface view and hide starting window are happen at the same
        // frame.
        final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
        transaction.hide(startingWindowLeash);

        decorView.getViewRootImpl().applyTransactionOnDraw(transaction);
        view.syncTransferSurfaceOnDraw();
        // Tell server we can remove the starting window
        decorView.postOnAnimation(() -> reportSplashscreenViewShown(token, view));
    }

    /**
+3 −4
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.content.res.Configuration;
import android.os.IBinder;
import android.util.MergedConfiguration;
import android.view.DisplayAdjustments.FixedRotationAdjustments;
import android.view.SurfaceControl;
import android.window.SplashScreenView.SplashScreenViewParcelable;

import com.android.internal.annotations.VisibleForTesting;
@@ -165,10 +166,8 @@ public abstract class ClientTransactionHandler {

    /** Attach a splash screen window view to the top of the activity */
    public abstract void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
            @NonNull SplashScreenViewParcelable parcelable);

    /** Hand over the splash screen window view to the activity */
    public abstract void handOverSplashScreenView(@NonNull ActivityClientRecord r);
            @NonNull SplashScreenViewParcelable parcelable,
            @NonNull SurfaceControl startingWindowLeash);

    /** Perform activity launch. */
    public abstract Activity handleLaunchActivity(@NonNull ActivityClientRecord r,
+9 −29
Original line number Diff line number Diff line
@@ -16,17 +16,14 @@

package android.app.servertransaction;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.ClientTransactionHandler;
import android.os.Parcel;
import android.view.SurfaceControl;
import android.window.SplashScreenView.SplashScreenViewParcelable;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Transfer a splash screen view to an Activity.
 * @hide
@@ -34,31 +31,13 @@ import java.lang.annotation.RetentionPolicy;
public class TransferSplashScreenViewStateItem extends ActivityTransactionItem {

    private SplashScreenViewParcelable mSplashScreenViewParcelable;
    private @TransferRequest int mRequest;

    @IntDef(value = {
            ATTACH_TO,
            HANDOVER_TO
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface TransferRequest {}
    // request client to attach the view on it.
    public static final int ATTACH_TO = 0;
    // tell client that you can handle the splash screen view.
    public static final int HANDOVER_TO = 1;
    private SurfaceControl mStartingWindowLeash;

    @Override
    public void execute(@NonNull ClientTransactionHandler client,
            @NonNull ActivityThread.ActivityClientRecord r,
            PendingTransactionActions pendingActions) {
        switch (mRequest) {
            case ATTACH_TO:
                client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable);
                break;
            case HANDOVER_TO:
                client.handOverSplashScreenView(r);
                break;
        }
        client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable, mStartingWindowLeash);
    }

    @Override
@@ -68,26 +47,27 @@ public class TransferSplashScreenViewStateItem extends ActivityTransactionItem {

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mRequest);
        dest.writeTypedObject(mSplashScreenViewParcelable, flags);
        dest.writeTypedObject(mStartingWindowLeash, flags);
    }

    private TransferSplashScreenViewStateItem() {}
    private TransferSplashScreenViewStateItem(Parcel in) {
        mRequest = in.readInt();
        mSplashScreenViewParcelable = in.readTypedObject(SplashScreenViewParcelable.CREATOR);
        mStartingWindowLeash = in.readTypedObject(SurfaceControl.CREATOR);
    }

    /** Obtain an instance initialized with provided params. */
    public static TransferSplashScreenViewStateItem obtain(@TransferRequest int state,
            @Nullable SplashScreenViewParcelable parcelable) {
    public static TransferSplashScreenViewStateItem obtain(
            @Nullable SplashScreenViewParcelable parcelable,
            @Nullable SurfaceControl startingWindowLeash) {
        TransferSplashScreenViewStateItem instance =
                ObjectPool.obtain(TransferSplashScreenViewStateItem.class);
        if (instance == null) {
            instance = new TransferSplashScreenViewStateItem();
        }
        instance.mRequest = state;
        instance.mSplashScreenViewParcelable = parcelable;
        instance.mStartingWindowLeash = startingWindowLeash;

        return instance;
    }
+29 −2
Original line number Diff line number Diff line
@@ -1889,18 +1889,45 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
     * @param p The SurfacePackage to embed.
     */
    public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
        setChildSurfacePackage(p, false /* applyTransactionOnDraw */);
    }

    /**
     * Similar to setChildSurfacePackage, but using the BLAST queue so the transaction can be
     * synchronized with the ViewRootImpl frame.
     * @hide
     */
    public void setChildSurfacePackageOnDraw(
            @NonNull SurfaceControlViewHost.SurfacePackage p) {
        setChildSurfacePackage(p, true /* applyTransactionOnDraw */);
    }

    /**
     * @param applyTransactionOnDraw Whether to apply transaction at onDraw or immediately.
     */
    private void setChildSurfacePackage(
            @NonNull SurfaceControlViewHost.SurfacePackage p, boolean applyTransactionOnDraw) {
        final SurfaceControl lastSc = mSurfacePackage != null ?
                mSurfacePackage.getSurfaceControl() : null;
        if (mSurfaceControl != null && lastSc != null) {
            mTmpTransaction.reparent(lastSc, null).apply();
            mTmpTransaction.reparent(lastSc, null);
            mSurfacePackage.release();
            applyTransaction(applyTransactionOnDraw);
        } else if (mSurfaceControl != null) {
            reparentSurfacePackage(mTmpTransaction, p);
            mTmpTransaction.apply();
            applyTransaction(applyTransactionOnDraw);
        }
        mSurfacePackage = p;
    }

    private void applyTransaction(boolean applyTransactionOnDraw) {
        if (applyTransactionOnDraw) {
            getViewRootImpl().applyTransactionOnDraw(mTmpTransaction);
        } else {
            mTmpTransaction.apply();
        }
    }

    private void reparentSurfacePackage(SurfaceControl.Transaction t,
            SurfaceControlViewHost.SurfacePackage p) {
        final SurfaceControl sc = p.getSurfaceControl();
Loading