diff --git a/api/current.txt b/api/current.txt index 7d39c7efe95ba..7330737252d00 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 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); - 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); + method public deprecated void setActions(java.util.List); + 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); - 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 CREATOR; + field public static final android.os.Parcelable.Creator CREATOR; + field protected android.util.Rational mAspectRatio; + field protected android.graphics.Rect mSourceRectHint; + field protected java.util.List 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); + 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 { diff --git a/api/system-current.txt b/api/system-current.txt index c8b35d165898b..e9f340765d0a0 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -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 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); - 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); + method public deprecated void setActions(java.util.List); + 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); - 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 CREATOR; + field public static final android.os.Parcelable.Creator CREATOR; + field protected android.util.Rational mAspectRatio; + field protected android.graphics.Rect mSourceRectHint; + field protected java.util.List 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); + 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 { diff --git a/api/test-current.txt b/api/test-current.txt index 374c20ea45dcd..8fd1c65635ffc 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -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 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); - 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); + method public deprecated void setActions(java.util.List); + 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); - 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 CREATOR; + field public static final android.os.Parcelable.Creator CREATOR; + field protected android.util.Rational mAspectRatio; + field protected android.graphics.Rect mSourceRectHint; + field protected java.util.List 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); + 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 { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index cdfb52b132f1f..e621c01748f92 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -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); } } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 1ab45b56e5caa..6e7b7504d4f31 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -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; } /** diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index d1eea3bdfb694..5ee8e0c258f74 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -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); /** diff --git a/core/java/android/app/PictureInPictureArgs.java b/core/java/android/app/PictureInPictureArgs.java index 2fa6360469013..63db86aac9a88 100644 --- a/core/java/android/app/PictureInPictureArgs.java +++ b/core/java/android/app/PictureInPictureArgs.java @@ -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 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 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 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 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 CREATOR = - new Creator() { - public PictureInPictureArgs createFromParcel(Parcel in) { - return new PictureInPictureArgs(in); - } - public PictureInPictureArgs[] newArray(int size) { - return new PictureInPictureArgs[size]; - } - }; } \ No newline at end of file diff --git a/core/java/android/app/PictureInPictureArgs.aidl b/core/java/android/app/PictureInPictureParams.aidl similarity index 94% rename from core/java/android/app/PictureInPictureArgs.aidl rename to core/java/android/app/PictureInPictureParams.aidl index 49df39a73a7d7..eaf5f06825f5b 100644 --- a/core/java/android/app/PictureInPictureArgs.aidl +++ b/core/java/android/app/PictureInPictureParams.aidl @@ -16,4 +16,4 @@ package android.app; -parcelable PictureInPictureArgs; +parcelable PictureInPictureParams; diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java new file mode 100644 index 0000000000000..875d592722678 --- /dev/null +++ b/core/java/android/app/PictureInPictureParams.java @@ -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 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 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 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 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 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 CREATOR = + new Creator() { + public PictureInPictureParams createFromParcel(Parcel in) { + return new PictureInPictureParams(in); + } + public PictureInPictureParams[] newArray(int size) { + return new PictureInPictureParams[size]; + } + }; +} \ No newline at end of file diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5bc3013d0e01e..bef74e85170a0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -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 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; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 5f554114f7176..158175f4b0d95 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -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.