Improve PIP enter transition w/ gesture nav (2/N)

Allow Launcher to query destination bounds of PiP window from SysUI.
Add also the callback notifies SysUI that the swipe-pip-to-home
operation is finished.

PipTaskOrganizer would ignore the entering transition if
swipe-pip-to-home should happen in Launcher.

Next: handle the swipe-pip-to-home transition in Launcher

Bug: 143965596
Test: N/A
Change-Id: Ia8bbdfcd4ed92c7ed968be84f0bf991feac8598e
This commit is contained in:
Hongwei Wang
2020-10-01 16:37:13 -07:00
parent ed528d6eba
commit cf1d1b853a
5 changed files with 178 additions and 21 deletions

View File

@@ -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;
}
}

View File

@@ -104,7 +104,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
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 extends TaskOrganizer implements ShellTaskOrganize
*/
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 extends TaskOrganizer implements ShellTaskOrganize
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 extends TaskOrganizer implements ShellTaskOrganize
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 extends TaskOrganizer implements ShellTaskOrganize
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 extends TaskOrganizer implements ShellTaskOrganize
@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 extends TaskOrganizer implements ShellTaskOrganize
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);

View File

@@ -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)) {

View File

@@ -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;
}

View File

@@ -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) {