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

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

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

parents 17e1eec6 1895b133
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -74,13 +74,18 @@ public abstract class Pip2Module {
            ShellController shellController,
            DisplayController displayController,
            DisplayInsetsController displayInsetsController,
            PipDisplayLayoutState pipDisplayLayoutState) {
            PipBoundsState pipBoundsState,
            PipBoundsAlgorithm pipBoundsAlgorithm,
            PipDisplayLayoutState pipDisplayLayoutState,
            PipScheduler pipScheduler,
            @ShellMainThread ShellExecutor mainExecutor) {
        if (!PipUtils.isPip2ExperimentEnabled()) {
            return Optional.empty();
        } else {
            return Optional.ofNullable(PipController.create(
                    context, shellInit, shellController, displayController, displayInsetsController,
                    pipDisplayLayoutState));
                    pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState, pipScheduler,
                    mainExecutor));
        }
    }

+137 −4
Original line number Diff line number Diff line
@@ -18,14 +18,31 @@ package com.android.wm.shell.pip2.phone;

import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;

import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;

import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.view.InsetsState;
import android.view.SurfaceControl;

import androidx.annotation.BinderThread;

import com.android.internal.protolog.common.ProtoLog;
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.pip.IPip;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -37,32 +54,54 @@ import com.android.wm.shell.sysui.ShellInit;
 * Manages the picture-in-picture (PIP) UI and states for Phones.
 */
public class PipController implements ConfigurationChangeListener,
        DisplayController.OnDisplaysChangedListener {
        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 PipController(Context context,
            ShellInit shellInit,
            ShellController shellController,
            DisplayController displayController,
            DisplayInsetsController displayInsetsController,
            PipDisplayLayoutState pipDisplayLayoutState) {
            PipBoundsState pipBoundsState,
            PipBoundsAlgorithm pipBoundsAlgorithm,
            PipDisplayLayoutState pipDisplayLayoutState,
            PipScheduler pipScheduler,
            ShellExecutor mainExecutor) {
        mContext = context;
        mShellController = shellController;
        mDisplayController = displayController;
        mDisplayInsetsController = displayInsetsController;
        mPipBoundsState = pipBoundsState;
        mPipBoundsAlgorithm = pipBoundsAlgorithm;
        mPipDisplayLayoutState = pipDisplayLayoutState;
        mPipScheduler = pipScheduler;
        mMainExecutor = mainExecutor;

        if (PipUtils.isPip2ExperimentEnabled()) {
            shellInit.addInitCallback(this::onInit, this);
        }
    }

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

    @Override
    public ShellExecutor getRemoteCallExecutor() {
        return mMainExecutor;
    }

    private void onInit() {
        // Ensure that we have the display info in case we get calls to update the bounds before the
        // listener calls back
@@ -80,6 +119,10 @@ public class PipController implements ConfigurationChangeListener,
                                        .getDisplayLayout(mPipDisplayLayoutState.getDisplayId()));
                    }
                });

        // Allow other outside processes to bind to PiP controller using the key below.
        mShellController.addExternalInterface(KEY_EXTRA_SHELL_PIP,
                this::createExternalInterface, this);
    }

    /**
@@ -90,16 +133,24 @@ public class PipController implements ConfigurationChangeListener,
            ShellController shellController,
            DisplayController displayController,
            DisplayInsetsController displayInsetsController,
            PipDisplayLayoutState pipDisplayLayoutState) {
            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;
        }
        return new PipController(context, shellInit, shellController, displayController,
                displayInsetsController, pipDisplayLayoutState);
                displayInsetsController, pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState,
                pipScheduler, mainExecutor);
    }

    private ExternalInterfaceBinder createExternalInterface() {
        return new IPipImpl(this);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfiguration) {
@@ -130,4 +181,86 @@ public class PipController implements ConfigurationChangeListener,
    private void onDisplayChanged(DisplayLayout layout) {
        mPipDisplayLayoutState.setDisplayLayout(layout);
    }

    private Rect getSwipePipToHomeBounds(ComponentName componentName, ActivityInfo activityInfo,
            PictureInPictureParams pictureInPictureParams,
            int launcherRotation, Rect hotseatKeepClearArea) {
        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                "getSwipePipToHomeBounds: %s", componentName);
        mPipBoundsState.setBoundsStateForEntry(componentName, activityInfo, pictureInPictureParams,
                mPipBoundsAlgorithm);
        return mPipBoundsAlgorithm.getEntryDestinationBounds();
    }

    private void onSwipePipToHomeAnimationStart(int taskId, ComponentName componentName,
            Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                "onSwipePipToHomeAnimationStart: %s", componentName);
        mPipScheduler.setInSwipePipToHomeTransition(true);
        // TODO: cache the overlay if provided for reparenting later.
    }

    /**
     * The interface for calls from outside the host process.
     */
    @BinderThread
    private static class IPipImpl extends IPip.Stub implements ExternalInterfaceBinder {
        private PipController mController;

        IPipImpl(PipController controller) {
            mController = controller;
        }

        /**
         * Invalidates this instance, preventing future calls from updating the controller.
         */
        @Override
        public void invalidate() {
            mController = null;
        }

        @Override
        public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
                PictureInPictureParams pictureInPictureParams, int launcherRotation,
                Rect keepClearArea) {
            Rect[] result = new Rect[1];
            executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome",
                    (controller) -> {
                        result[0] = controller.getSwipePipToHomeBounds(componentName, activityInfo,
                                pictureInPictureParams, launcherRotation, keepClearArea);
                    }, true /* blocking */);
            return result[0];
        }

        @Override
        public void stopSwipePipToHome(int taskId, ComponentName componentName,
                Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
            if (overlay != null) {
                overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
            }
            executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
                    (controller) -> controller.onSwipePipToHomeAnimationStart(
                            taskId, componentName, destinationBounds, overlay, appBounds));
        }

        @Override
        public void abortSwipePipToHome(int taskId, ComponentName componentName) {}

        @Override
        public void setShelfHeight(boolean visible, int height) {}

        @Override
        public void setLauncherKeepClearAreaHeight(boolean visible, int height) {}

        @Override
        public void setLauncherAppIconSize(int iconSizePx) {}

        @Override
        public void setPipAnimationListener(IPipAnimationListener listener) {
            // TODO: set a proper animation listener to update the Launcher state as needed.
        }

        @Override
        public void setPipAnimationTypeToAlpha() {}
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -63,6 +63,9 @@ public class PipScheduler {
    @Nullable
    private SurfaceControl mPinnedTaskLeash;

    // true if Launcher has started swipe PiP to home animation
    private boolean mInSwipePipToHomeTransition;

    /**
     * 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
@@ -168,6 +171,14 @@ public class PipScheduler {
        mPipTransitionController.startResizeTransition(wct, onFinishResizeCallback);
    }

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

    boolean isInSwipePipToHomeTransition() {
        return mInSwipePipToHomeTransition;
    }

    void onExitPip() {
        mPipTaskToken = null;
        mPinnedTaskLeash = null;
+25 −0
Original line number Diff line number Diff line
@@ -152,6 +152,12 @@ public class PipTransition extends PipTransitionController {
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        if (transition == mEnterTransition) {
            mEnterTransition = null;
            if (mPipScheduler.isInSwipePipToHomeTransition()) {
                // If this is the second transition as a part of swipe PiP to home cuj,
                // handle this transition as a special case with no-op animation.
                return handleSwipePipToHomeTransition(info, startTransaction, finishTransaction,
                        finishCallback);
            }
            if (isLegacyEnter(info)) {
                // If this is a legacy-enter-pip (auto-enter is off and PiP activity went to pause),
                // then we should run an ALPHA type (cross-fade) animation.
@@ -207,6 +213,25 @@ public class PipTransition extends PipTransitionController {
        return true;
    }

    private boolean handleSwipePipToHomeTransition(@NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
            @NonNull SurfaceControl.Transaction finishTransaction,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        TransitionInfo.Change pipChange = getPipChange(info);
        if (pipChange == null) {
            return false;
        }
        mPipScheduler.setInSwipePipToHomeTransition(false);
        mPipTaskToken = pipChange.getContainer();

        // cache the PiP task token and leash
        mPipScheduler.setPipTaskToken(mPipTaskToken);

        startTransaction.apply();
        finishCallback.onTransitionFinished(null);
        return true;
    }

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