diff options
5 files changed, 178 insertions, 21 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index 7c2625133cad..488f9092b7da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -18,7 +18,10 @@ package com.android.wm.shell.pip; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.PictureInPictureParams; import android.content.ComponentName; +import android.content.pm.ActivityInfo; +import android.graphics.Rect; import android.media.session.MediaController; import com.android.wm.shell.pip.phone.PipTouchHandler; @@ -232,4 +235,28 @@ public interface Pip { */ default void suspendPipResizing(int reason) { } + + /** + * Called by Launcher when swiping an auto-pip enabled Activity to home starts + * @param componentName {@link ComponentName} represents the Activity entering PiP + * @param activityInfo {@link ActivityInfo} tied to the Activity + * @param pictureInPictureParams {@link PictureInPictureParams} tied to the Activity + * @param launcherRotation Rotation Launcher is in + * @param shelfHeight Shelf height when landing PiP window onto Launcher + * @return Destination bounds of PiP window based on the parameters passed in + */ + default Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + return null; + } + + /** + * Called by Launcher when swiping an auto-pip enable Activity to home finishes + * @param componentName {@link ComponentName} represents the Activity entering PiP + * @param destinationBounds Destination bounds of PiP window + */ + default void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + return; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index e8e1be7e6400..a17cd8c6f9af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -104,7 +104,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, UNDEFINED(0), TASK_APPEARED(1), ENTERING_PIP(2), - EXITING_PIP(3); + ENTERED_PIP(3), + EXITING_PIP(4); private final int mStateValue; @@ -241,6 +242,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ private boolean mShouldDeferEnteringPip; + /** + * If set to {@code true}, no entering PiP transition would be kicked off and most likely + * it's due to the fact that Launcher is handling the transition directly when swiping + * auto PiP-able Activity to home. + * See also {@link #startSwipePipToHome(ComponentName, ActivityInfo, PictureInPictureParams)}. + */ + private boolean mShouldIgnoreEnteringPipTransition; + public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, @@ -309,6 +318,27 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** + * Callback when Launcher starts swipe-pip-to-home operation. + * @return {@link Rect} for destination bounds. + */ + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams) { + mShouldIgnoreEnteringPipTransition = true; + mState = State.ENTERING_PIP; + return mPipBoundsHandler.getDestinationBounds(componentName, + getAspectRatioOrDefault(pictureInPictureParams), + null /* bounds */, getMinimalSize(activityInfo)); + } + + /** + * Callback when launcher finishes swipe-pip-to-home operation. + * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards. + */ + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + mLastReportedBounds.set(destinationBounds); + } + + /** * Expands PiP to the previous bounds, this is done in two phases using * {@link WindowContainerTransaction} * - setActivityWindowingMode to either fullscreen or split-secondary at beginning of the @@ -435,6 +465,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER); + if (mShouldIgnoreEnteringPipTransition) { + // Animation has been finished together with Recents, directly apply the sync + // transaction to PiP here. + applyEnterPipSyncTransaction(mLastReportedBounds, () -> { + mState = State.ENTERED_PIP; + }); + mShouldIgnoreEnteringPipTransition = false; + return; + } + if (mShouldDeferEnteringPip) { if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing"); // if deferred, hide the surface till fixed rotation is completed @@ -489,6 +529,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.apply(); + applyEnterPipSyncTransaction(destinationBounds, () -> { + mUpdateHandler.post(() -> mPipAnimationController + .getAnimator(mLeash, destinationBounds, 0f, 1f) + .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(durationMs) + .start()); + // mState is set right after the animation is kicked off to block any resize + // requests such as offsetPip that may have been called prior to the transition. + mState = State.ENTERING_PIP; + }); + } + + private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) { final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setBounds(mToken, destinationBounds); @@ -497,15 +551,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onTransactionReady(int id, SurfaceControl.Transaction t) { t.apply(); - mUpdateHandler.post(() -> mPipAnimationController - .getAnimator(mLeash, destinationBounds, 0f, 1f) - .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) - .setPipAnimationCallback(mPipAnimationCallback) - .setDuration(durationMs) - .start()); - // mState is set right after the animation is kicked off to block any resize - // requests such as offsetPip that may have been called prior to the transition. - mState = State.ENTERING_PIP; + if (runnable != null) { + runnable.run(); + } } }); } @@ -523,6 +571,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void sendOnPipTransitionFinished( @PipAnimationController.TransitionDirection int direction) { + if (direction == TRANSITION_DIRECTION_TO_PIP) { + mState = State.ENTERED_PIP; + } runOnMainHandler(() -> { for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 5193656e8299..41c0a881c039 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -22,9 +22,11 @@ import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.PictureInPictureParams; import android.app.RemoteAction; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.ParceledListSlice; import android.graphics.Rect; import android.os.Handler; @@ -87,7 +89,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } // If there is an animation running (ie. from a shelf offset), then ensure that we calculate // the bounds for the next orientation using the destination bounds of the animation - // TODO: Techincally this should account for movement animation bounds as well + // TODO: Technically this should account for movement animation bounds as well Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds(); final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mContext, mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation, @@ -351,16 +353,18 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac */ @Override public void setShelfHeight(boolean visible, int height) { - mHandler.post(() -> { - final int shelfHeight = visible ? height : 0; - final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight); - if (changed) { - mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight); - updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(), - false /* fromRotation */, false /* fromImeAdjustment */, - true /* fromShelfAdjustment */, null /* windowContainerTransaction */); - } - }); + mHandler.post(() -> setShelfHeightLocked(visible, height)); + } + + private void setShelfHeightLocked(boolean visible, int height) { + final int shelfHeight = visible ? height : 0; + final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight); + if (changed) { + mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight); + updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(), + false /* fromRotation */, false /* fromImeAdjustment */, + true /* fromShelfAdjustment */, null /* windowContainerTransaction */); + } } @Override @@ -374,6 +378,21 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } @Override + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight); + mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, launcherRotation); + return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams); + } + + @Override + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds); + } + + @Override public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { if (isOutPipDirection(direction)) { // Exiting PIP, save the reentry bounds to restore to when re-entering. diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index c94bcaaf7383..e4427f49e030 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -16,6 +16,9 @@ package com.android.systemui.shared.recents; +import android.app.PictureInPictureParams; +import android.content.ComponentName; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; @@ -166,4 +169,27 @@ interface ISystemUiProxy { * Notifies to expand notification panel. */ void expandNotificationPanel() = 29; + + /** + * Notifies that Activity is about to be swiped to home with entering PiP transition and + * queries the destination bounds for PiP depends on Launcher's rotation and shelf height. + * + * @param componentName ComponentName represents the Activity + * @param activityInfo ActivityInfo tied to the Activity + * @param pictureInPictureParams PictureInPictureParams tied to the Activity + * @param launcherRotation Launcher rotation to calculate the PiP destination bounds + * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds + * @return destination bounds the PiP window should land into + */ + Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo, + in PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) = 30; + + /** + * Notifies the swiping Activity to PiP onto home transition is finished + * + * @param componentName ComponentName represents the Activity + * @param destinationBounds the destination bounds the PiP window lands into + */ + void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 0ae1170a25fc..3b0415b31eb5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -35,12 +35,14 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_T import android.annotation.FloatRange; import android.app.ActivityTaskManager; +import android.app.PictureInPictureParams; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; @@ -503,6 +505,38 @@ public class OverviewProxyService extends CurrentUserTracker implements } } + @Override + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + if (!verifyCaller("startSwipePipToHome") || !mHasPipFeature) { + return null; + } + long binderToken = Binder.clearCallingIdentity(); + try { + return mPipOptional.map(pip -> + pip.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams, launcherRotation, shelfHeight)) + .orElse(null); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + if (!verifyCaller("stopSwipePipToHome") || !mHasPipFeature) { + return; + } + long binderToken = Binder.clearCallingIdentity(); + try { + mPipOptional.ifPresent(pip -> pip.stopSwipePipToHome( + componentName, destinationBounds)); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { |