Merge "Improve PIP enter transition w/ gesture nav (2/N)"
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -308,6 +317,27 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
|
||||
mOneShotAnimationType = animationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -373,6 +377,21 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
|
||||
mHandler.post(() -> mPinnedStackAnimationRecentsCallback = callback);
|
||||
}
|
||||
|
||||
@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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user