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

Commit 4c17efbf authored by Ikram Gabiyev's avatar Ikram Gabiyev Committed by Android (Google) Code Review
Browse files

Merge "Implement auto-enter PiP2 in gesture nav [5/N]" into main

parents b3416f90 fde6406e
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -93,8 +93,9 @@ public abstract class Pip2Module {
    @Provides
    static PipScheduler providePipScheduler(Context context,
            PipBoundsState pipBoundsState,
            @ShellMainThread ShellExecutor mainExecutor) {
        return new PipScheduler(context, pipBoundsState, mainExecutor);
            @ShellMainThread ShellExecutor mainExecutor,
            ShellTaskOrganizer shellTaskOrganizer) {
        return new PipScheduler(context, pipBoundsState, mainExecutor, shellTaskOrganizer);
    }

    @WMSingleton
+2 −2
Original line number Diff line number Diff line
@@ -245,9 +245,9 @@ public class PipController implements ConfigurationChangeListener,
            Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                "onSwipePipToHomeAnimationStart: %s", componentName);
        mPipScheduler.setInSwipePipToHomeTransition(true);
        mPipScheduler.onSwipePipToHomeAnimationStart(taskId, componentName, destinationBounds,
                overlay, appBounds);
        mPipRecentsAnimationListener.onPipAnimationStarted();
        // TODO: cache the overlay if provided for reparenting later.
    }

    //
+41 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -30,9 +31,11 @@ import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipUtils;
@@ -52,6 +55,7 @@ public class PipScheduler {
    private final Context mContext;
    private final PipBoundsState mPipBoundsState;
    private final ShellExecutor mMainExecutor;
    private final ShellTaskOrganizer mShellTaskOrganizer;
    private PipSchedulerReceiver mSchedulerReceiver;
    private PipTransitionController mPipTransitionController;

@@ -66,6 +70,16 @@ public class PipScheduler {
    // true if Launcher has started swipe PiP to home animation
    private boolean mInSwipePipToHomeTransition;

    // Overlay leash potentially used during swipe PiP to home transition;
    // if null while mInSwipePipToHomeTransition is true, then srcRectHint was invalid.
    @Nullable
    SurfaceControl mSwipePipToHomeOverlay;

    // App bounds used when as a starting point to swipe PiP to home animation in Launcher;
    // these are also used to calculate the app icon overlay buffer size.
    @NonNull
    final Rect mSwipePipToHomeAppBounds = new Rect();

    /**
     * Temporary PiP CUJ codes to schedule PiP related transitions directly from Shell.
     * This is used for a broadcast receiver to resolve intents. This should be removed once
@@ -101,11 +115,14 @@ public class PipScheduler {
        }
    }

    public PipScheduler(Context context, PipBoundsState pipBoundsState,
            ShellExecutor mainExecutor) {
    public PipScheduler(Context context,
            PipBoundsState pipBoundsState,
            ShellExecutor mainExecutor,
            ShellTaskOrganizer shellTaskOrganizer) {
        mContext = context;
        mPipBoundsState = pipBoundsState;
        mMainExecutor = mainExecutor;
        mShellTaskOrganizer = shellTaskOrganizer;

        if (PipUtils.isPip2ExperimentEnabled()) {
            // temporary broadcast receiver to initiate exit PiP via expand
@@ -115,6 +132,10 @@ public class PipScheduler {
        }
    }

    ShellExecutor getMainExecutor() {
        return mMainExecutor;
    }

    void setPipTransitionController(PipTransitionController pipTransitionController) {
        mPipTransitionController = pipTransitionController;
    }
@@ -171,6 +192,24 @@ public class PipScheduler {
        mPipTransitionController.startResizeTransition(wct, onFinishResizeCallback);
    }

    void onSwipePipToHomeAnimationStart(int taskId, ComponentName componentName,
            Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
        mInSwipePipToHomeTransition = true;
        mSwipePipToHomeOverlay = overlay;
        mSwipePipToHomeAppBounds.set(appBounds);
        if (overlay != null) {
            // Shell transitions might use a root animation leash, which will be removed when
            // the Recents transition is finished. Launcher attaches the overlay leash to this
            // animation target leash; thus, we need to reparent it to the actual Task surface now.
            // PipTransition is responsible to fade it out and cleanup when finishing the enter PIP
            // transition.
            SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
            mShellTaskOrganizer.reparentChildSurfaceToTask(taskId, overlay, tx);
            tx.setLayer(overlay, Integer.MAX_VALUE);
            tx.apply();
        }
    }

    void setInSwipePipToHomeTransition(boolean inSwipePipToHome) {
        mInSwipePipToHomeTransition = inSwipePipToHome;
    }
+64 −3
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_RESIZE_PIP;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
@@ -44,6 +47,7 @@ import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipContentOverlay;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -55,6 +59,11 @@ import java.util.function.Consumer;
 */
public class PipTransition extends PipTransitionController {
    private static final String TAG = PipTransition.class.getSimpleName();
    /**
     * The fixed start delay in ms when fading out the content overlay from bounds animation.
     * The fadeout animation is guaranteed to start after the client has drawn under the new config.
     */
    private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 400;

    private final Context mContext;
    private final PipScheduler mPipScheduler;
@@ -230,10 +239,13 @@ public class PipTransition extends PipTransitionController {

        PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
        Rect srcRectHint = params.getSourceRectHint();
        Rect startBounds = pipChange.getStartAbsBounds();
        Rect destinationBounds = pipChange.getEndAbsBounds();

        WindowContainerTransaction finishWct = new WindowContainerTransaction();

        if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) {
            float scale = (float) destinationBounds.width() / srcRectHint.width();
            final float scale = (float) destinationBounds.width() / srcRectHint.width();
            startTransaction.setWindowCrop(pipLeash, srcRectHint);
            startTransaction.setPosition(pipLeash,
                    destinationBounds.left - srcRectHint.left * scale,
@@ -244,13 +256,62 @@ public class PipTransition extends PipTransitionController {
            // in multi-activity case, reparenting yields new reset scales coming from pinned task.
            startTransaction.setScale(pipLeash, scale, scale);
        } else {
            // TODO(b/325481148): handle the case with invalid srcRectHint (using overlay).
            final float scaleX = (float) destinationBounds.width() / startBounds.width();
            final float scaleY = (float) destinationBounds.height() / startBounds.height();
            final int overlaySize = PipContentOverlay.PipAppIconOverlay
                    .getOverlaySize(mPipScheduler.mSwipePipToHomeAppBounds, destinationBounds);
            SurfaceControl overlayLeash = mPipScheduler.mSwipePipToHomeOverlay;

            startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
                    .setScale(pipLeash, scaleX, scaleY)
                    .setWindowCrop(pipLeash, startBounds)
                    .reparent(overlayLeash, pipLeash)
                    .setLayer(overlayLeash, Integer.MAX_VALUE);

            if (mPipTaskToken != null) {
                SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
                tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
                                this::onClientDrawAtTransitionEnd)
                        .setScale(overlayLeash, 1f, 1f)
                        .setPosition(overlayLeash,
                                (destinationBounds.width() - overlaySize) / 2f,
                                (destinationBounds.height() - overlaySize) / 2f);
                finishWct.setBoundsChangeTransaction(mPipTaskToken, tx);
            }
        }
        startTransaction.apply();
        finishCallback.onTransitionFinished(null);

        // Note that finishWct should be free of any actual WM state changes; we are using
        // it for syncing with the client draw after delayed configuration changes are dispatched.
        finishCallback.onTransitionFinished(finishWct.isEmpty() ? null : finishWct);
        return true;
    }

    private void onClientDrawAtTransitionEnd() {
        startOverlayFadeoutAnimation();
    }

    private void startOverlayFadeoutAnimation() {
        ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f);
        animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DELAY_MS);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
                tx.remove(mPipScheduler.mSwipePipToHomeOverlay);
                tx.apply();
                mPipScheduler.mSwipePipToHomeOverlay = null;
            }
        });
        animator.addUpdateListener(animation -> {
            float alpha = (float) animation.getAnimatedValue();
            SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
            tx.setAlpha(mPipScheduler.mSwipePipToHomeOverlay, alpha).apply();
        });
        animator.start();
    }

    private boolean startBoundsTypeEnterAnimation(@NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,