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

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

Merge changes Id6bdbd19,I06e36862 into main

* changes:
  Implement auto-enter PiP2 in gesture nav [3/N]
  Implement auto-enter PiP2 in gesture nav [2/N]
parents 72c9d1a6 9ec4199d
Loading
Loading
Loading
Loading
+137 −42
Original line number Diff line number Diff line
@@ -32,13 +32,16 @@ import android.view.SurfaceControl;

import androidx.annotation.BinderThread;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.pip.IPip;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -57,15 +60,40 @@ public class PipController implements ConfigurationChangeListener,
        DisplayController.OnDisplaysChangedListener, RemoteCallable<PipController> {
    private static final String TAG = PipController.class.getSimpleName();

    private Context mContext;
    private ShellController mShellController;
    private DisplayController mDisplayController;
    private DisplayInsetsController mDisplayInsetsController;
    private PipBoundsState mPipBoundsState;
    private PipBoundsAlgorithm mPipBoundsAlgorithm;
    private PipDisplayLayoutState mPipDisplayLayoutState;
    private PipScheduler mPipScheduler;
    private ShellExecutor mMainExecutor;
    private final Context mContext;
    private final ShellController mShellController;
    private final DisplayController mDisplayController;
    private final DisplayInsetsController mDisplayInsetsController;
    private final PipBoundsState mPipBoundsState;
    private final PipBoundsAlgorithm mPipBoundsAlgorithm;
    private final PipDisplayLayoutState mPipDisplayLayoutState;
    private final PipScheduler mPipScheduler;
    private final ShellExecutor mMainExecutor;

    // Wrapper for making Binder calls into PiP animation listener hosted in launcher's Recents.
    private PipAnimationListener mPipRecentsAnimationListener;

    @VisibleForTesting
    interface PipAnimationListener {
        /**
         * Notifies the listener that the Pip animation is started.
         */
        void onPipAnimationStarted();

        /**
         * Notifies the listener about PiP resource dimensions changed.
         * Listener can expect an immediate callback the first time they attach.
         *
         * @param cornerRadius the pixel value of the corner radius, zero means it's disabled.
         * @param shadowRadius the pixel value of the shadow radius, zero means it's disabled.
         */
        void onPipResourceDimensionsChanged(int cornerRadius, int shadowRadius);

        /**
         * Notifies the listener that user leaves PiP by tapping on the expand button.
         */
        void onExpandPip();
    }

    private PipController(Context context,
            ShellInit shellInit,
@@ -92,14 +120,27 @@ public class PipController implements ConfigurationChangeListener,
        }
    }

    @Override
    public Context getContext() {
        return mContext;
    /**
     * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
     */
    public static PipController create(Context context,
            ShellInit shellInit,
            ShellController shellController,
            DisplayController displayController,
            DisplayInsetsController displayInsetsController,
            PipBoundsState pipBoundsState,
            PipBoundsAlgorithm pipBoundsAlgorithm,
            PipDisplayLayoutState pipDisplayLayoutState,
            PipScheduler pipScheduler,
            ShellExecutor mainExecutor) {
        if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
            ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Device doesn't support Pip feature", TAG);
            return null;
        }

    @Override
    public ShellExecutor getRemoteCallExecutor() {
        return mMainExecutor;
        return new PipController(context, shellInit, shellController, displayController,
                displayInsetsController, pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState,
                pipScheduler, mainExecutor);
    }

    private void onInit() {
@@ -109,7 +150,6 @@ public class PipController implements ConfigurationChangeListener,
        DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
        mPipDisplayLayoutState.setDisplayLayout(layout);

        mShellController.addConfigurationChangeListener(this);
        mDisplayController.addDisplayWindowListener(this);
        mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
                new DisplayInsetsController.OnInsetsChangedListener() {
@@ -123,45 +163,50 @@ public class PipController implements ConfigurationChangeListener,
        // Allow other outside processes to bind to PiP controller using the key below.
        mShellController.addExternalInterface(KEY_EXTRA_SHELL_PIP,
                this::createExternalInterface, this);
        mShellController.addConfigurationChangeListener(this);
    }

    /**
     * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
     */
    public static PipController create(Context context,
            ShellInit shellInit,
            ShellController shellController,
            DisplayController displayController,
            DisplayInsetsController displayInsetsController,
            PipBoundsState pipBoundsState,
            PipBoundsAlgorithm pipBoundsAlgorithm,
            PipDisplayLayoutState pipDisplayLayoutState,
            PipScheduler pipScheduler,
            ShellExecutor mainExecutor) {
        if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
            ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Device doesn't support Pip feature", TAG);
            return null;
    private ExternalInterfaceBinder createExternalInterface() {
        return new IPipImpl(this);
    }
        return new PipController(context, shellInit, shellController, displayController,
                displayInsetsController, pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState,
                pipScheduler, mainExecutor);

    //
    // RemoteCallable implementations
    //

    @Override
    public Context getContext() {
        return mContext;
    }

    private ExternalInterfaceBinder createExternalInterface() {
        return new IPipImpl(this);
    @Override
    public ShellExecutor getRemoteCallExecutor() {
        return mMainExecutor;
    }

    //
    // ConfigurationChangeListener implementations
    //

    @Override
    public void onConfigurationChanged(Configuration newConfiguration) {
        mPipDisplayLayoutState.onConfigurationChanged();
    }

    @Override
    public void onDensityOrFontScaleChanged() {
        onPipResourceDimensionsChanged();
    }

    @Override
    public void onThemeChanged() {
        onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()));
    }

    //
    // DisplayController.OnDisplaysChangedListener implementations
    //

    @Override
    public void onDisplayAdded(int displayId) {
        if (displayId != mPipDisplayLayoutState.getDisplayId()) {
@@ -182,6 +227,10 @@ public class PipController implements ConfigurationChangeListener,
        mPipDisplayLayoutState.setDisplayLayout(layout);
    }

    //
    // IPip Binder stub helpers
    //

    private Rect getSwipePipToHomeBounds(ComponentName componentName, ActivityInfo activityInfo,
            PictureInPictureParams pictureInPictureParams,
            int launcherRotation, Rect hotseatKeepClearArea) {
@@ -197,18 +246,56 @@ public class PipController implements ConfigurationChangeListener,
        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                "onSwipePipToHomeAnimationStart: %s", componentName);
        mPipScheduler.setInSwipePipToHomeTransition(true);
        mPipRecentsAnimationListener.onPipAnimationStarted();
        // TODO: cache the overlay if provided for reparenting later.
    }

    //
    // IPipAnimationListener Binder proxy helpers
    //

    private void setPipRecentsAnimationListener(PipAnimationListener pipAnimationListener) {
        mPipRecentsAnimationListener = pipAnimationListener;
        onPipResourceDimensionsChanged();
    }

    private void onPipResourceDimensionsChanged() {
        if (mPipRecentsAnimationListener != null) {
            mPipRecentsAnimationListener.onPipResourceDimensionsChanged(
                    mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius),
                    mContext.getResources().getDimensionPixelSize(R.dimen.pip_shadow_radius));
        }
    }

    /**
     * The interface for calls from outside the host process.
     */
    @BinderThread
    private static class IPipImpl extends IPip.Stub implements ExternalInterfaceBinder {
        private PipController mController;
        private final SingleInstanceRemoteListener<PipController, IPipAnimationListener> mListener;
        private final PipAnimationListener mPipAnimationListener = new PipAnimationListener() {
            @Override
            public void onPipAnimationStarted() {
                mListener.call(l -> l.onPipAnimationStarted());
            }

            @Override
            public void onPipResourceDimensionsChanged(int cornerRadius, int shadowRadius) {
                mListener.call(l -> l.onPipResourceDimensionsChanged(cornerRadius, shadowRadius));
            }

            @Override
            public void onExpandPip() {
                mListener.call(l -> l.onExpandPip());
            }
        };

        IPipImpl(PipController controller) {
            mController = controller;
            mListener = new SingleInstanceRemoteListener<>(mController,
                    (cntrl) -> cntrl.setPipRecentsAnimationListener(mPipAnimationListener),
                    (cntrl) -> cntrl.setPipRecentsAnimationListener(null));
        }

        /**
@@ -217,6 +304,7 @@ public class PipController implements ConfigurationChangeListener,
        @Override
        public void invalidate() {
            mController = null;
            mListener.unregister();
        }

        @Override
@@ -257,7 +345,14 @@ public class PipController implements ConfigurationChangeListener,

        @Override
        public void setPipAnimationListener(IPipAnimationListener listener) {
            // TODO: set a proper animation listener to update the Launcher state as needed.
            executeRemoteCallWithTaskPermission(mController, "setPipAnimationListener",
                    (controller) -> {
                        if (listener != null) {
                            mListener.register(listener);
                        } else {
                            mListener.unregister();
                        }
                    });
        }

        @Override
+1 −1
Original line number Diff line number Diff line
@@ -172,7 +172,7 @@ public class PipScheduler {
    }

    void setInSwipePipToHomeTransition(boolean inSwipePipToHome) {
        mInSwipePipToHomeTransition = true;
        mInSwipePipToHomeTransition = inSwipePipToHome;
    }

    boolean isInSwipePipToHomeTransition() {
+20 −0
Original line number Diff line number Diff line
@@ -226,7 +226,26 @@ public class PipTransition extends PipTransitionController {

        // cache the PiP task token and leash
        mPipScheduler.setPipTaskToken(mPipTaskToken);
        SurfaceControl pipLeash = pipChange.getLeash();

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

        if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) {
            float scale = (float) destinationBounds.width() / srcRectHint.width();
            startTransaction.setWindowCrop(pipLeash, srcRectHint);
            startTransaction.setPosition(pipLeash,
                    destinationBounds.left - srcRectHint.left * scale,
                    destinationBounds.top - srcRectHint.top * scale);

            // Reset the scale in case we are in the multi-activity case.
            // TO_FRONT transition already scales down the task in single-activity case, but
            // 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).
        }
        startTransaction.apply();
        finishCallback.onTransitionFinished(null);
        return true;
@@ -303,6 +322,7 @@ public class PipTransition extends PipTransitionController {

        WindowContainerTransaction wct = new WindowContainerTransaction();
        wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds);
        wct.deferConfigToTransitionEnd(pipTask.token);
        return wct;
    }