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

Commit 91e0ba18 authored by Ikram Gabiyev's avatar Ikram Gabiyev
Browse files

Implement auto-enter PiP2 in gesture nav [2/N]

Implement auto-enter PiP in PiP2 while in gesture
navigation mode.

Allow Launcher to register its PinnedStackListener
with ...pip2.phone.PipController.

Bug: 325481148
Test: manually swipe up to enter PiP in auto-enter
Change-Id: I06e3686219768606f464ea1bb7268fdbe0f133e3
parent ac316924
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() {