Updating PiP API and documentation.

- Renaming PictureInPictureArgs to PictureInPictureParams, making it
  immutable and built with a builder class.  In addition, making the
  aspect ratio rational.
- Moving the check to get max number of actions to the activity to have
  more flexibility going forward
- Also ensures that enterPictureInPictureMode() returns true if the
  activity is already in PiP mode.
- Will follow up with CL to remove PictureInPictureArgs and related methods
  once this CL drops

Bug: 35765211
Bug: 37638323
Bug: 37637423
Bug: 37636674
Bug: 36342780
Test: android.server.cts.ActivityManagerPinnedStackTests
Test: android.app.cts.PictureInPictureParamsBuilderTest

Change-Id: I54a487d55a59c9ce8ac7bd6c80c61209c8b26224
Signed-off-by: Winson Chung <winsonc@google.com>
This commit is contained in:
Winson Chung
2017-04-25 11:00:48 -07:00
parent 62bb027d12
commit 709904f800
11 changed files with 578 additions and 314 deletions

View File

@@ -3584,8 +3584,9 @@ package android.app {
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public void enterPictureInPictureMode();
method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
method public deprecated void enterPictureInPictureMode();
method public deprecated boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
method public boolean enterPictureInPictureMode(android.app.PictureInPictureParams);
method public <T extends android.view.View> T findViewById(int);
method public void finish();
method public void finishActivity(int);
@@ -3609,6 +3610,7 @@ package android.app {
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
method public android.view.MenuInflater getMenuInflater();
method public final android.app.Activity getParent();
@@ -3760,6 +3762,7 @@ package android.app {
method public void setIntent(android.content.Intent);
method public final void setMediaController(android.media.session.MediaController);
method public void setPictureInPictureArgs(android.app.PictureInPictureArgs);
method public void setPictureInPictureParams(android.app.PictureInPictureParams);
method public final deprecated void setProgress(int);
method public final deprecated void setProgressBarIndeterminate(boolean);
method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -3838,7 +3841,7 @@ package android.app {
method public int getLauncherLargeIconDensity();
method public int getLauncherLargeIconSize();
method public int getLockTaskModeState();
method public static int getMaxNumPictureInPictureActions();
method public static deprecated int getMaxNumPictureInPictureActions();
method public int getMemoryClass();
method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
@@ -5695,16 +5698,29 @@ package android.app {
method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
}
public final class PictureInPictureArgs implements android.os.Parcelable {
ctor public PictureInPictureArgs();
ctor public PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
method public android.app.PictureInPictureArgs clone();
public final deprecated class PictureInPictureArgs extends android.app.PictureInPictureParams {
ctor public deprecated PictureInPictureArgs();
ctor public deprecated PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
method public deprecated void setActions(java.util.List<android.app.RemoteAction>);
method public deprecated void setAspectRatio(float);
method public deprecated void setSourceRectHint(android.graphics.Rect);
}
public class PictureInPictureParams implements android.os.Parcelable {
method public int describeContents();
method public void setActions(java.util.List<android.app.RemoteAction>);
method public void setAspectRatio(float);
method public void setSourceRectHint(android.graphics.Rect);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
field public static final android.os.Parcelable.Creator<android.app.PictureInPictureParams> CREATOR;
field protected android.util.Rational mAspectRatio;
field protected android.graphics.Rect mSourceRectHint;
field protected java.util.List<android.app.RemoteAction> mUserActions;
}
public static class PictureInPictureParams.Builder {
ctor public PictureInPictureParams.Builder();
method public android.app.PictureInPictureParams build();
method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
}
public class Presentation extends android.app.Dialog {

View File

@@ -3715,8 +3715,9 @@ package android.app {
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public void enterPictureInPictureMode();
method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
method public deprecated void enterPictureInPictureMode();
method public deprecated boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
method public boolean enterPictureInPictureMode(android.app.PictureInPictureParams);
method public <T extends android.view.View> T findViewById(int);
method public void finish();
method public void finishActivity(int);
@@ -3740,6 +3741,7 @@ package android.app {
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
method public android.view.MenuInflater getMenuInflater();
method public final android.app.Activity getParent();
@@ -3894,6 +3896,7 @@ package android.app {
method public void setIntent(android.content.Intent);
method public final void setMediaController(android.media.session.MediaController);
method public void setPictureInPictureArgs(android.app.PictureInPictureArgs);
method public void setPictureInPictureParams(android.app.PictureInPictureParams);
method public final deprecated void setProgress(int);
method public final deprecated void setProgressBarIndeterminate(boolean);
method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -3979,7 +3982,7 @@ package android.app {
method public int getLauncherLargeIconDensity();
method public int getLauncherLargeIconSize();
method public int getLockTaskModeState();
method public static int getMaxNumPictureInPictureActions();
method public static deprecated int getMaxNumPictureInPictureActions();
method public int getMemoryClass();
method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
@@ -5898,16 +5901,29 @@ package android.app {
method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
}
public final class PictureInPictureArgs implements android.os.Parcelable {
ctor public PictureInPictureArgs();
ctor public PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
method public android.app.PictureInPictureArgs clone();
public final deprecated class PictureInPictureArgs extends android.app.PictureInPictureParams {
ctor public deprecated PictureInPictureArgs();
ctor public deprecated PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
method public deprecated void setActions(java.util.List<android.app.RemoteAction>);
method public deprecated void setAspectRatio(float);
method public deprecated void setSourceRectHint(android.graphics.Rect);
}
public class PictureInPictureParams implements android.os.Parcelable {
method public int describeContents();
method public void setActions(java.util.List<android.app.RemoteAction>);
method public void setAspectRatio(float);
method public void setSourceRectHint(android.graphics.Rect);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
field public static final android.os.Parcelable.Creator<android.app.PictureInPictureParams> CREATOR;
field protected android.util.Rational mAspectRatio;
field protected android.graphics.Rect mSourceRectHint;
field protected java.util.List<android.app.RemoteAction> mUserActions;
}
public static class PictureInPictureParams.Builder {
ctor public PictureInPictureParams.Builder();
method public android.app.PictureInPictureParams build();
method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
}
public class Presentation extends android.app.Dialog {

View File

@@ -3586,8 +3586,9 @@ package android.app {
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public void enterPictureInPictureMode();
method public boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
method public deprecated void enterPictureInPictureMode();
method public deprecated boolean enterPictureInPictureMode(android.app.PictureInPictureArgs);
method public boolean enterPictureInPictureMode(android.app.PictureInPictureParams);
method public <T extends android.view.View> T findViewById(int);
method public void finish();
method public void finishActivity(int);
@@ -3611,6 +3612,7 @@ package android.app {
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
method public android.view.MenuInflater getMenuInflater();
method public final android.app.Activity getParent();
@@ -3762,6 +3764,7 @@ package android.app {
method public void setIntent(android.content.Intent);
method public final void setMediaController(android.media.session.MediaController);
method public void setPictureInPictureArgs(android.app.PictureInPictureArgs);
method public void setPictureInPictureParams(android.app.PictureInPictureParams);
method public final deprecated void setProgress(int);
method public final deprecated void setProgressBarIndeterminate(boolean);
method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -3841,7 +3844,7 @@ package android.app {
method public int getLauncherLargeIconDensity();
method public int getLauncherLargeIconSize();
method public int getLockTaskModeState();
method public static int getMaxNumPictureInPictureActions();
method public static deprecated int getMaxNumPictureInPictureActions();
method public int getMemoryClass();
method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
@@ -5709,16 +5712,29 @@ package android.app {
method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
}
public final class PictureInPictureArgs implements android.os.Parcelable {
ctor public PictureInPictureArgs();
ctor public PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
method public android.app.PictureInPictureArgs clone();
public final deprecated class PictureInPictureArgs extends android.app.PictureInPictureParams {
ctor public deprecated PictureInPictureArgs();
ctor public deprecated PictureInPictureArgs(float, java.util.List<android.app.RemoteAction>);
method public deprecated void setActions(java.util.List<android.app.RemoteAction>);
method public deprecated void setAspectRatio(float);
method public deprecated void setSourceRectHint(android.graphics.Rect);
}
public class PictureInPictureParams implements android.os.Parcelable {
method public int describeContents();
method public void setActions(java.util.List<android.app.RemoteAction>);
method public void setAspectRatio(float);
method public void setSourceRectHint(android.graphics.Rect);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.PictureInPictureArgs> CREATOR;
field public static final android.os.Parcelable.Creator<android.app.PictureInPictureParams> CREATOR;
field protected android.util.Rational mAspectRatio;
field protected android.graphics.Rect mSourceRectHint;
field protected java.util.List<android.app.RemoteAction> mUserActions;
}
public static class PictureInPictureParams.Builder {
ctor public PictureInPictureParams.Builder();
method public android.app.PictureInPictureParams build();
method public android.app.PictureInPictureParams.Builder setActions(java.util.List<android.app.RemoteAction>);
method public android.app.PictureInPictureParams.Builder setAspectRatio(android.util.Rational);
method public android.app.PictureInPictureParams.Builder setSourceRectHint(android.graphics.Rect);
}
public class Presentation extends android.app.Dialog {

View File

@@ -2039,70 +2039,112 @@ public class Activity extends ContextThemeWrapper
/**
* Puts the activity in picture-in-picture mode if possible in the current system state. Any
* prior calls to {@link #setPictureInPictureArgs(PictureInPictureArgs)} will still apply when
* entering picture-in-picture through this call.
* prior calls to {@link #setPictureInPictureParams(PictureInPictureParams)} will still apply
* when entering picture-in-picture through this call.
*
* @see #enterPictureInPictureMode(PictureInPictureArgs)
* @see #enterPictureInPictureMode(PictureInPictureParams)
* @see android.R.attr#supportsPictureInPicture
*/
@Deprecated
public void enterPictureInPictureMode() {
enterPictureInPictureMode(new PictureInPictureArgs());
enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
}
/**
* Puts the activity in picture-in-picture mode if possible in the current system state with
* explicit given arguments. Only the set parameters in {@param args} will override prior calls
* {@link #setPictureInPictureArgs(PictureInPictureArgs)}.
*
* The system may disallow entering picture-in-picture in various cases, including when the
* activity is not visible.
*
* @see android.R.attr#supportsPictureInPicture
*
* @param args the explicit non-null arguments to use when entering picture-in-picture.
* @return whether the system successfully entered picture-in-picture.
* TO BE REMOVED
*/
@Deprecated
public boolean enterPictureInPictureMode(@NonNull PictureInPictureArgs args) {
try {
if (args == null) {
throw new IllegalArgumentException("Expected non-null picture-in-picture args");
}
updatePictureInPictureArgsForContentInsets(args);
updatePictureInPictureParamsForContentInsets(args);
return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, args);
} catch (RemoteException e) {
return false;
}
}
/**
* Puts the activity in picture-in-picture mode if possible in the current system state. The
* set parameters in {@param params} will be combined with the parameters from prior calls to
* {@link #setPictureInPictureParams(PictureInPictureParams)}.
*
* The system may disallow entering picture-in-picture in various cases, including when the
* activity is not visible, if the screen is locked or if the user has an activity pinned.
*
* @see android.R.attr#supportsPictureInPicture
* @see PictureInPictureParams
*
* @param params non-null parameters to be combined with previously set parameters when entering
* picture-in-picture.
*
* @return true if the system puts this activity into picture-in-picture mode or was already
* in picture-in-picture mode (@see {@link #isInPictureInPictureMode())
*/
public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
try {
if (params == null) {
throw new IllegalArgumentException("Expected non-null picture-in-picture params");
}
updatePictureInPictureParamsForContentInsets(params);
return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, params);
} catch (RemoteException e) {
return false;
}
}
/**
* TO BE REMOVED
*/
public void setPictureInPictureArgs(@NonNull PictureInPictureArgs args) {
setPictureInPictureParams(args);
}
/**
* Updates the properties of the picture-in-picture activity, or sets it to be used later when
* {@link #enterPictureInPictureMode()} is called.
*
* @param args the new properties of the picture-in-picture.
* @param params the new parameters for the picture-in-picture.
*/
public void setPictureInPictureArgs(@NonNull PictureInPictureArgs args) {
public void setPictureInPictureParams(@NonNull PictureInPictureParams params) {
try {
if (args == null) {
throw new IllegalArgumentException("Expected non-null picture-in-picture args");
if (params == null) {
throw new IllegalArgumentException("Expected non-null picture-in-picture params");
}
updatePictureInPictureArgsForContentInsets(args);
ActivityManagerNative.getDefault().setPictureInPictureArgs(mToken, args);
updatePictureInPictureParamsForContentInsets(params);
ActivityManagerNative.getDefault().setPictureInPictureParams(mToken, params);
} catch (RemoteException e) {
}
}
/**
* Updates the provided {@param args} with the last known content insets for this activity, to
* Return the number of actions that will be displayed in the picture-in-picture UI when the
* user interacts with the activity currently in picture-in-picture mode. This number may change
* if the global configuration changes (ie. if the device is plugged into an external display),
* but will always be larger than three.
*/
public int getMaxNumPictureInPictureActions() {
try {
return ActivityManagerNative.getDefault().getMaxNumPictureInPictureActions(mToken);
} catch (RemoteException e) {
return 0;
}
}
/**
* Updates the provided {@param params} with the last known content insets for this activity, to
* be used with the source hint rect for the transition into PiP.
*/
private void updatePictureInPictureArgsForContentInsets(PictureInPictureArgs args) {
if (args != null && args.hasSourceBoundsHint() && getWindow() != null &&
private void updatePictureInPictureParamsForContentInsets(PictureInPictureParams params) {
if (params != null && params.hasSourceBoundsHint() && getWindow() != null &&
getWindow().peekDecorView() != null &&
getWindow().peekDecorView().getViewRootImpl() != null) {
args.setSourceRectHintInsets(
params.setSourceRectHintInsets(
getWindow().peekDecorView().getViewRootImpl().getLastContentInsets());
} else {
args.setSourceRectHintInsets(null);
params.setSourceRectHintInsets(null);
}
}

View File

@@ -122,8 +122,6 @@ public class ActivityManager {
private static int gMaxRecentTasks = -1;
private static final int NUM_ALLOWED_PIP_ACTIONS = 3;
private final Context mContext;
private static volatile boolean sSystemReady = false;
@@ -1127,11 +1125,15 @@ public class ActivityManager {
}
/**
* Return the maximum number of actions that will be displayed in the picture-in-picture UI when
* the user interacts with the activity currently in picture-in-picture mode.
* Return the number of actions that will be displayed in the picture-in-picture UI when the
* user interacts with the activity currently in picture-in-picture mode. This number may change
* if the global configuration changes (ie. if the device is plugged into an external display).
*
* TO BE REMOVED
*/
@Deprecated
public static int getMaxNumPictureInPictureActions() {
return NUM_ALLOWED_PIP_ACTIONS;
return 3;
}
/**

View File

@@ -34,7 +34,7 @@ import android.app.IUidObserver;
import android.app.IUserSwitchObserver;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.PictureInPictureArgs;
import android.app.PictureInPictureParams;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.assist.AssistContent;
@@ -500,8 +500,9 @@ interface IActivityManager {
boolean isInMultiWindowMode(in IBinder token);
boolean isInPictureInPictureMode(in IBinder token);
void killPackageDependents(in String packageName, int userId);
boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureArgs args);
void setPictureInPictureArgs(in IBinder token, in PictureInPictureArgs args);
boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
int getMaxNumPictureInPictureActions(in IBinder token);
void activityRelaunched(in IBinder token);
IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
/**

View File

@@ -16,65 +16,24 @@
package android.app;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Rational;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a set of arguments used to initialize the picture-in-picture mode.
* TO BE REMOVED
*/
public final class PictureInPictureArgs implements Parcelable {
/**
* The expected aspect ratio of the picture-in-picture.
*/
@Nullable
private Float mAspectRatio;
/**
* The set of actions that are associated with this activity when in picture-in-picture.
*/
@Nullable
private List<RemoteAction> mUserActions;
/**
* The source bounds hint used when entering picture-in-picture, relative to the window bounds.
* We can use this internally for the transition into picture-in-picture to ensure that a
* particular source rect is visible throughout the whole transition.
*/
@Nullable
private Rect mSourceRectHint;
/**
* The content insets that are used with the source hint rect for the transition into PiP where
* the insets are removed at the beginning of the transition.
*/
@Nullable
private Rect mSourceRectHintInsets;
PictureInPictureArgs(Parcel in) {
if (in.readInt() != 0) {
mAspectRatio = in.readFloat();
}
if (in.readInt() != 0) {
mUserActions = new ArrayList<>();
in.readParcelableList(mUserActions, RemoteAction.class.getClassLoader());
}
if (in.readInt() != 0) {
mSourceRectHint = Rect.CREATOR.createFromParcel(in);
}
if (in.readInt() != 0) {
mSourceRectHintInsets = Rect.CREATOR.createFromParcel(in);
}
}
@Deprecated
public final class PictureInPictureArgs extends PictureInPictureParams {
/**
* Creates a new set of picture-in-picture arguments.
*
* TODO: Remove once we remove PictureInPictureArgs.
*/
@Deprecated
public PictureInPictureArgs() {
// Empty constructor
}
@@ -82,64 +41,43 @@ public final class PictureInPictureArgs implements Parcelable {
/**
* Creates a new set of picture-in-picture arguments from the given {@param aspectRatio} and
* {@param actions}.
*
* TODO: Remove once we remove PictureInPictureArgs.
*/
@Deprecated
public PictureInPictureArgs(float aspectRatio, List<RemoteAction> actions) {
mAspectRatio = aspectRatio;
setAspectRatio(aspectRatio);
if (actions != null) {
mUserActions = new ArrayList<>(actions);
}
}
/**
* Copies the set parameters from the other picture-in-picture args.
* @hide
*/
public void copyOnlySet(PictureInPictureArgs otherArgs) {
if (otherArgs.hasSetAspectRatio()) {
mAspectRatio = otherArgs.mAspectRatio;
}
if (otherArgs.hasSetActions()) {
mUserActions = otherArgs.mUserActions;
}
if (otherArgs.hasSourceBoundsHint()) {
mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
}
if (otherArgs.hasSourceBoundsHintInsets()) {
mSourceRectHintInsets = new Rect(otherArgs.getSourceRectHintInsets());
setActions(actions);
}
}
/**
* Sets the aspect ratio.
* @param aspectRatio the new aspect ratio for picture-in-picture.
*
* @param aspectRatio the new aspect ratio for picture-in-picture, must be within 2.39:1 and
* 1:2.39.
*
* TODO: Remove once we remove PictureInPictureArgs.
*/
@Deprecated
public void setAspectRatio(float aspectRatio) {
mAspectRatio = aspectRatio;
// Temporary workaround
mAspectRatio = new Rational((int) (aspectRatio * 1000000000), 1000000000);
}
/**
* @return the aspect ratio. If none is set, return 0.
* @hide
*/
public float getAspectRatio() {
if (mAspectRatio != null) {
return mAspectRatio;
}
return 0f;
}
/**
* @return whether the aspect ratio is set.
* @hide
*/
public boolean hasSetAspectRatio() {
return mAspectRatio != null;
}
/**
* Sets the user actions.
* Sets the user actions. If there are more than
* {@link ActivityManager#getMaxNumPictureInPictureActions()} actions, then the input will be
* truncated to that number.
*
* @param actions the new actions to show in the picture-in-picture menu.
*
* @see RemoteAction
*
* TODO: Remove once we remove PictureInPictureArgs.
*/
@Deprecated
public void setActions(List<RemoteAction> actions) {
if (mUserActions != null) {
mUserActions = null;
@@ -149,28 +87,15 @@ public final class PictureInPictureArgs implements Parcelable {
}
}
/**
* @return the set of user actions.
* @hide
*/
public List<RemoteAction> getActions() {
return mUserActions;
}
/**
* @return whether the user actions are set.
* @hide
*/
public boolean hasSetActions() {
return mUserActions != null;
}
/**
* Sets the source bounds hint. These bounds are only used when an activity first enters
* picture-in-picture, and describe the bounds in window coordinates of activity entering
* picture-in-picture that will be visible following the transition. For the best effect, these
* bounds should also match the aspect ratio in the arguments.
*
* TODO: Remove once we remove PictureInPictureArgs.
*/
@Deprecated
public void setSourceRectHint(Rect launchBounds) {
if (launchBounds == null) {
mSourceRectHint = null;
@@ -178,103 +103,4 @@ public final class PictureInPictureArgs implements Parcelable {
mSourceRectHint = new Rect(launchBounds);
}
}
/**
* Sets the insets to be used with the source rect hint bounds.
* @hide
*/
public void setSourceRectHintInsets(Rect insets) {
if (insets == null) {
mSourceRectHintInsets = null;
} else {
mSourceRectHintInsets = new Rect(insets);
}
}
/**
* @return the source rect hint
* @hide
*/
public Rect getSourceRectHint() {
return mSourceRectHint;
}
/**
* @return the source rect hint insets.
* @hide
*/
public Rect getSourceRectHintInsets() {
return mSourceRectHintInsets;
}
/**
* @return whether there are launch bounds set
* @hide
*/
public boolean hasSourceBoundsHint() {
return mSourceRectHint != null && !mSourceRectHint.isEmpty();
}
/**
* @return whether there are source rect hint insets set
* @hide
*/
public boolean hasSourceBoundsHintInsets() {
return mSourceRectHintInsets != null;
}
@Override
public PictureInPictureArgs clone() {
PictureInPictureArgs args = new PictureInPictureArgs(mAspectRatio, mUserActions);
if (mSourceRectHint != null) {
args.setSourceRectHint(mSourceRectHint);
}
if (mSourceRectHintInsets != null) {
args.setSourceRectHintInsets(mSourceRectHintInsets);
}
return args;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
if (mAspectRatio != null) {
out.writeInt(1);
out.writeFloat(mAspectRatio);
} else {
out.writeInt(0);
}
if (mUserActions != null) {
out.writeInt(1);
out.writeParcelableList(mUserActions, 0);
} else {
out.writeInt(0);
}
if (mSourceRectHint != null) {
out.writeInt(1);
mSourceRectHint.writeToParcel(out, 0);
} else {
out.writeInt(0);
}
if (mSourceRectHintInsets != null) {
out.writeInt(1);
mSourceRectHintInsets.writeToParcel(out, 0);
} else {
out.writeInt(0);
}
}
public static final Creator<PictureInPictureArgs> CREATOR =
new Creator<PictureInPictureArgs>() {
public PictureInPictureArgs createFromParcel(Parcel in) {
return new PictureInPictureArgs(in);
}
public PictureInPictureArgs[] newArray(int size) {
return new PictureInPictureArgs[size];
}
};
}

View File

@@ -16,4 +16,4 @@
package android.app;
parcelable PictureInPictureArgs;
parcelable PictureInPictureParams;

View File

@@ -0,0 +1,333 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Rational;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a set of parameters used to initialize and update an Activity in picture-in-picture
* mode.
*
* TODO: Make this final after we remove PictureInPictureArgs
*/
public class PictureInPictureParams implements Parcelable {
/**
* Builder class for {@link PictureInPictureParams} objects.
*/
public static class Builder {
@Nullable
private Rational mAspectRatio;
@Nullable
private List<RemoteAction> mUserActions;
@Nullable
private Rect mSourceRectHint;
/**
* Sets the aspect ratio. This aspect ratio is defined as the desired width / height, and
* does not change upon device rotation.
*
* @param aspectRatio the new aspect ratio for the activity in picture-in-picture, must be
* between 2.39:1 and 1:2.39 (inclusive).
*
* @return this builder instance.
*/
public Builder setAspectRatio(Rational aspectRatio) {
mAspectRatio = aspectRatio;
return this;
}
/**
* Sets the user actions. If there are more than
* {@link ActivityManager#getMaxNumPictureInPictureActions()} actions, then the input list
* will be truncated to that number.
*
* @param actions the new actions to show in the picture-in-picture menu.
*
* @return this builder instance.
*
* @see RemoteAction
*/
public Builder setActions(List<RemoteAction> actions) {
if (mUserActions != null) {
mUserActions = null;
}
if (actions != null) {
mUserActions = new ArrayList<>(actions);
}
return this;
}
/**
* Sets the source bounds hint. These bounds are only used when an activity first enters
* picture-in-picture, and describe the bounds in window coordinates of activity entering
* picture-in-picture that will be visible following the transition. For the best effect,
* these bounds should also match the aspect ratio in the arguments.
*
* @param launchBounds window-coordinate bounds indicating the area of the activity that
* will still be visible following the transition into picture-in-picture (eg. the video
* view bounds in a video player)
*
* @return this builder instance.
*/
public Builder setSourceRectHint(Rect launchBounds) {
if (launchBounds == null) {
mSourceRectHint = null;
} else {
mSourceRectHint = new Rect(launchBounds);
}
return this;
}
/**
* @return an immutable {@link PictureInPictureParams} to be used when entering or updating
* the activity in picture-in-picture.
*
* @see Activity#enterPictureInPictureMode(PictureInPictureParams)
* @see Activity#setPictureInPictureParams(PictureInPictureParams)
*/
public PictureInPictureParams build() {
PictureInPictureParams params = new PictureInPictureParams(mAspectRatio, mUserActions,
mSourceRectHint);
return params;
}
}
/**
* The expected aspect ratio of the picture-in-picture.
*/
// TODO: Make private once we removed PictureInPictureArgs
@Nullable
protected Rational mAspectRatio;
/**
* The set of actions that are associated with this activity when in picture-in-picture.
*/
// TODO: Make private once we removed PictureInPictureArgs
@Nullable
protected List<RemoteAction> mUserActions;
/**
* The source bounds hint used when entering picture-in-picture, relative to the window bounds.
* We can use this internally for the transition into picture-in-picture to ensure that a
* particular source rect is visible throughout the whole transition.
*/
// TODO: Make private once we removed PictureInPictureArgs
@Nullable
protected Rect mSourceRectHint;
/**
* The content insets that are used with the source hint rect for the transition into PiP where
* the insets are removed at the beginning of the transition.
*/
@Nullable
private Rect mSourceRectHintInsets;
/**
* TO BE REMOVED
*/
@Deprecated
PictureInPictureParams() {
// TODO: Remove once we remove PictureInPictureArgs
}
private PictureInPictureParams(Parcel in) {
if (in.readInt() != 0) {
mAspectRatio = new Rational(in.readInt(), in.readInt());
}
if (in.readInt() != 0) {
mUserActions = new ArrayList<>();
in.readParcelableList(mUserActions, RemoteAction.class.getClassLoader());
}
if (in.readInt() != 0) {
mSourceRectHint = Rect.CREATOR.createFromParcel(in);
}
if (in.readInt() != 0) {
mSourceRectHintInsets = Rect.CREATOR.createFromParcel(in);
}
}
private PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions,
Rect sourceRectHint) {
mAspectRatio = aspectRatio;
mUserActions = actions;
mSourceRectHint = sourceRectHint;
}
/**
* Copies the set parameters from the other picture-in-picture args.
* @hide
*/
public void copyOnlySet(PictureInPictureParams otherArgs) {
if (otherArgs.hasSetAspectRatio()) {
mAspectRatio = otherArgs.mAspectRatio;
}
if (otherArgs.hasSetActions()) {
mUserActions = otherArgs.mUserActions;
}
if (otherArgs.hasSourceBoundsHint()) {
mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
}
if (otherArgs.hasSourceBoundsHintInsets()) {
mSourceRectHintInsets = new Rect(otherArgs.getSourceRectHintInsets());
}
}
/**
* @return the aspect ratio. If none is set, return 0.
* @hide
*/
public float getAspectRatio() {
if (mAspectRatio != null) {
return mAspectRatio.floatValue();
}
return 0f;
}
/**
* @return whether the aspect ratio is set.
* @hide
*/
public boolean hasSetAspectRatio() {
return mAspectRatio != null;
}
/**
* @return the set of user actions.
* @hide
*/
public List<RemoteAction> getActions() {
return mUserActions;
}
/**
* @return whether the user actions are set.
* @hide
*/
public boolean hasSetActions() {
return mUserActions != null;
}
/**
* Truncates the set of actions to the given {@param size}.
* @hide
*/
public void truncateActions(int size) {
if (hasSetActions()) {
mUserActions = mUserActions.subList(0, size);
}
}
/**
* Sets the insets to be used with the source rect hint bounds.
* @hide
*/
@Deprecated
public void setSourceRectHintInsets(Rect insets) {
if (insets == null) {
mSourceRectHintInsets = null;
} else {
mSourceRectHintInsets = new Rect(insets);
}
}
/**
* @return the source rect hint
* @hide
*/
public Rect getSourceRectHint() {
return mSourceRectHint;
}
/**
* @return the source rect hint insets.
* @hide
*/
public Rect getSourceRectHintInsets() {
return mSourceRectHintInsets;
}
/**
* @return whether there are launch bounds set
* @hide
*/
public boolean hasSourceBoundsHint() {
return mSourceRectHint != null && !mSourceRectHint.isEmpty();
}
/**
* @return whether there are source rect hint insets set
* @hide
*/
public boolean hasSourceBoundsHintInsets() {
return mSourceRectHintInsets != null;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
if (mAspectRatio != null) {
out.writeInt(1);
out.writeInt(mAspectRatio.getNumerator());
out.writeInt(mAspectRatio.getDenominator());
} else {
out.writeInt(0);
}
if (mUserActions != null) {
out.writeInt(1);
out.writeParcelableList(mUserActions, 0);
} else {
out.writeInt(0);
}
if (mSourceRectHint != null) {
out.writeInt(1);
mSourceRectHint.writeToParcel(out, 0);
} else {
out.writeInt(0);
}
if (mSourceRectHintInsets != null) {
out.writeInt(1);
mSourceRectHintInsets.writeToParcel(out, 0);
} else {
out.writeInt(0);
}
}
public static final Creator<PictureInPictureParams> CREATOR =
new Creator<PictureInPictureParams>() {
public PictureInPictureParams createFromParcel(Parcel in) {
return new PictureInPictureParams(in);
}
public PictureInPictureParams[] newArray(int size) {
return new PictureInPictureParams[size];
}
};
}

View File

@@ -229,7 +229,7 @@ import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.PictureInPictureArgs;
import android.app.PictureInPictureParams;
import android.app.ProfilerInfo;
import android.app.RemoteAction;
import android.app.WaitResult;
@@ -7818,29 +7818,38 @@ public class ActivityManagerService extends IActivityManager.Stub
final long origId = Binder.clearCallingIdentity();
try {
synchronized(this) {
final ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack == null || stack.mStackId != PINNED_STACK_ID) {
return false;
}
// If we are animating to fullscreen then we have already dispatched the PIP mode
// changed, so we should reflect that check here as well.
final PinnedStackWindowController windowController =
((PinnedActivityStack) stack).getWindowContainerController();
return !windowController.isAnimatingBoundsToFullscreen();
return isInPictureInPictureMode(ActivityRecord.forTokenLocked(token));
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
private boolean isInPictureInPictureMode(ActivityRecord r) {
if (r == null || r.getStack() == null || !r.getStack().isPinnedStack() ||
r.getStack().isInStackLocked(r) == null) {
return false;
}
// If we are animating to fullscreen then we have already dispatched the PIP mode
// changed, so we should reflect that check here as well.
final PinnedActivityStack stack = r.getStack();
final PinnedStackWindowController windowController = stack.getWindowContainerController();
return !windowController.isAnimatingBoundsToFullscreen();
}
@Override
public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureArgs args) {
public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized(this) {
final ActivityRecord r = ensureValidPictureInPictureActivityArgsLocked(
"enterPictureInPictureMode", token, args);
final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
"enterPictureInPictureMode", token, params);
// If the activity is already in picture in picture mode, then just return early
if (isInPictureInPictureMode(r)) {
return true;
}
// Activity supports picture-in-picture, now check that we can enter PiP at this
// point, if it is
@@ -7851,7 +7860,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final Runnable enterPipRunnable = () -> {
// Only update the saved args from the args that are set
r.pictureInPictureArgs.copyOnlySet(args);
r.pictureInPictureArgs.copyOnlySet(params);
final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
// Adjust the source bounds by the insets for the transition down
@@ -7870,7 +7879,7 @@ public class ActivityManagerService extends IActivityManager.Stub
MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
r.supportsPictureInPictureWhilePausing);
logPictureInPictureArgs(args);
logPictureInPictureArgs(params);
};
if (isKeyguardLocked()) {
@@ -7909,15 +7918,15 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
public void setPictureInPictureArgs(IBinder token, final PictureInPictureArgs args) {
public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized(this) {
final ActivityRecord r = ensureValidPictureInPictureActivityArgsLocked(
"setPictureInPictureArgs", token, args);
final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
"setPictureInPictureParams", token, params);
// Only update the saved args from the args that are set
r.pictureInPictureArgs.copyOnlySet(args);
r.pictureInPictureArgs.copyOnlySet(params);
if (r.getStack().getStackId() == PINNED_STACK_ID) {
// If the activity is already in picture-in-picture, update the pinned stack now
// if it is not already expanding to fullscreen. Otherwise, the arguments will
@@ -7929,21 +7938,28 @@ public class ActivityManagerService extends IActivityManager.Stub
stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
}
}
logPictureInPictureArgs(args);
logPictureInPictureArgs(params);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
private void logPictureInPictureArgs(PictureInPictureArgs args) {
if (args.hasSetActions()) {
@Override
public int getMaxNumPictureInPictureActions(IBinder token) {
// Currently, this is a static constant, but later, we may change this to be dependent on
// the context of the activity
return 3;
}
private void logPictureInPictureArgs(PictureInPictureParams params) {
if (params.hasSetActions()) {
MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count",
args.getActions().size());
params.getActions().size());
}
if (args.hasSetAspectRatio()) {
if (params.hasSetAspectRatio()) {
LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED);
lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, args.getAspectRatio());
lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio());
MetricsLogger.action(lm);
}
}
@@ -7954,8 +7970,8 @@ public class ActivityManagerService extends IActivityManager.Stub
*
* @return the activity record for the given {@param token} if all the checks pass.
*/
private ActivityRecord ensureValidPictureInPictureActivityArgsLocked(String caller,
IBinder token, PictureInPictureArgs args) {
private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
IBinder token, PictureInPictureParams params) {
if (!mSupportsPictureInPicture) {
throw new IllegalStateException(caller
+ ": Device doesn't support picture-in-picture mode.");
@@ -7977,9 +7993,9 @@ public class ActivityManagerService extends IActivityManager.Stub
+ ": Activities on the home, assistant, or recents stack not supported");
}
if (args.hasSetAspectRatio()
if (params.hasSetAspectRatio()
&& !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
args.getAspectRatio())) {
params.getAspectRatio())) {
final float minAspectRatio = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
final float maxAspectRatio = mContext.getResources().getFloat(
@@ -7989,12 +8005,8 @@ public class ActivityManagerService extends IActivityManager.Stub
minAspectRatio, maxAspectRatio));
}
if (args.hasSetActions()
&& args.getActions().size() > ActivityManager.getMaxNumPictureInPictureActions()) {
throw new IllegalArgumentException(String.format(caller + ": Invalid number of"
+ "picture-in-picture actions. Only a maximum of %d actions allowed",
ActivityManager.getMaxNumPictureInPictureActions()));
}
// Truncate the number of actions if necessary
params.truncateActions(getMaxNumPictureInPictureActions(token));
return r;
}

View File

@@ -117,7 +117,7 @@ import android.annotation.NonNull;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.PictureInPictureArgs;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
import android.content.ComponentName;
import android.content.Intent;
@@ -293,8 +293,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
boolean supportsPictureInPictureWhilePausing; // This flag is set by the system to indicate
// that the activity can enter picture in picture while pausing (ie. only when another
// task is brought to front or started)
PictureInPictureArgs pictureInPictureArgs = new PictureInPictureArgs(); // The PiP
// arguments used when deferring the entering of picture-in-picture.
PictureInPictureParams pictureInPictureArgs = new PictureInPictureParams.Builder().build();
// The PiP params used when deferring the entering of picture-in-picture.
int launchCount; // count of launches since last state
long lastLaunchTime; // time of last launch of this activity
ComponentName requestedVrComponent; // the requested component for handling VR mode.