1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.RequiresFeature;
22 import android.annotation.TestApi;
23 import android.content.pm.PackageManager;
24 import android.graphics.Rect;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.util.Rational;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Objects;
32 
33 /**
34  * Represents a set of parameters used to initialize and update an Activity in picture-in-picture
35  * mode.
36  */
37 public final class PictureInPictureParams implements Parcelable {
38 
39     /**
40      * Builder class for {@link PictureInPictureParams} objects.
41      */
42     public static class Builder {
43 
44         @Nullable
45         private Rational mAspectRatio;
46 
47         @Nullable
48         private Rational mExpandedAspectRatio;
49 
50         @Nullable
51         private List<RemoteAction> mUserActions;
52 
53         @Nullable
54         private RemoteAction mCloseAction;
55 
56         @Nullable
57         private Rect mSourceRectHint;
58 
59         private Boolean mAutoEnterEnabled;
60 
61         private Boolean mSeamlessResizeEnabled;
62 
63         private CharSequence mTitle;
64 
65         private CharSequence mSubtitle;
66 
67         private Boolean mIsLaunchIntoPip;
68 
69         /** Default constructor */
Builder()70         public Builder() {}
71 
72         /**
73          * Copy constructor
74          * @param original {@link PictureInPictureParams} instance this builder is built upon.
75          */
Builder(@onNull PictureInPictureParams original)76         public Builder(@NonNull PictureInPictureParams original) {
77             mAspectRatio = original.mAspectRatio;
78             mUserActions = original.mUserActions;
79             mCloseAction = original.mCloseAction;
80             mSourceRectHint = original.mSourceRectHint;
81             mAutoEnterEnabled = original.mAutoEnterEnabled;
82             mSeamlessResizeEnabled = original.mSeamlessResizeEnabled;
83             mTitle = original.mTitle;
84             mSubtitle = original.mSubtitle;
85             mIsLaunchIntoPip = original.mIsLaunchIntoPip;
86         }
87 
88         /**
89          * Sets the aspect ratio.  This aspect ratio is defined as the desired width / height, and
90          * does not change upon device rotation.
91          *
92          * @param aspectRatio the new aspect ratio for the activity in picture-in-picture, must be
93          *                    between 2.39:1 and 1:2.39 (inclusive).
94          * @return this builder instance.
95          */
setAspectRatio(Rational aspectRatio)96         public Builder setAspectRatio(Rational aspectRatio) {
97             mAspectRatio = aspectRatio;
98             return this;
99         }
100 
101         /**
102          * Sets the aspect ratio for the expanded picture-in-picture mode. The aspect ratio is
103          * defined as the desired width / height. <br/>
104          * The aspect ratio cannot be changed from horizontal to vertical or vertical to horizontal
105          * while the PIP is shown. Any such changes will be ignored. <br/>
106          *
107          * Setting the expanded ratio shows the activity's support for expanded mode.
108          *
109          * @param expandedAspectRatio must not be between 2.39:1 and 1:2.39 (inclusive). If {@code
110          *                            null}, expanded picture-in-picture mode is not supported.
111          * @return this builder instance.
112          */
113         @RequiresFeature(PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE)
setExpandedAspectRatio(@ullable Rational expandedAspectRatio)114         public @NonNull Builder setExpandedAspectRatio(@Nullable Rational expandedAspectRatio) {
115             mExpandedAspectRatio = expandedAspectRatio;
116             return this;
117         }
118 
119         /**
120          * Sets the user actions.  If there are more than
121          * {@link Activity#getMaxNumPictureInPictureActions()} actions, then the input list
122          * will be truncated to that number.
123          *
124          * @param actions the new actions to show in the picture-in-picture menu.
125          *
126          * @return this builder instance.
127          *
128          * @see RemoteAction
129          */
setActions(List<RemoteAction> actions)130         public Builder setActions(List<RemoteAction> actions) {
131             if (mUserActions != null) {
132                 mUserActions = null;
133             }
134             if (actions != null) {
135                 mUserActions = new ArrayList<>(actions);
136             }
137             return this;
138         }
139 
140         /**
141          * Sets a close action that should be invoked before the default close PiP action. The
142          * custom action must close the activity quickly using {@link Activity#finish()}.
143          * Otherwise, the system will forcibly close the PiP as if no custom close action was
144          * provided.
145          *
146          * If the action matches one set via {@link PictureInPictureParams.Builder#setActions(List)}
147          * it may be shown in place of that custom action in the menu.
148          *
149          * @param action to replace the system close action
150          * @return this builder instance.
151          * @see RemoteAction
152          */
153         @NonNull
setCloseAction(@ullable RemoteAction action)154         public Builder setCloseAction(@Nullable RemoteAction action) {
155             mCloseAction = action;
156             return this;
157         }
158 
159         /**
160          * Sets the window-coordinate bounds of an activity transitioning to picture-in-picture.
161          * The bounds is the area of an activity that will be visible in the transition to
162          * picture-in-picture mode. For the best effect, these bounds should also match the
163          * aspect ratio in the arguments.
164          *
165          * In Android 12+ these bounds are also reused to improve the exit transition from
166          * picture-in-picture mode. See
167          * <a href="{@docRoot}develop/ui/views/picture-in-picture#smoother-exit">Support
168          * smoother animations when exiting out of PiP mode</a> for more details.
169          *
170          * @param launchBounds window-coordinate bounds indicating the area of the activity that
171          * will still be visible following the transition into picture-in-picture (e.g. the video
172          * view bounds in a video player)
173          *
174          * @return this builder instance.
175          */
setSourceRectHint(Rect launchBounds)176         public Builder setSourceRectHint(Rect launchBounds) {
177             if (launchBounds == null) {
178                 mSourceRectHint = null;
179             } else {
180                 mSourceRectHint = new Rect(launchBounds);
181             }
182             return this;
183         }
184 
185         /**
186          * Sets whether the system will automatically put the activity in picture-in-picture mode
187          * without needing/waiting for the activity to call
188          * {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}.
189          *
190          * If true, {@link Activity#onPictureInPictureRequested()} will never be called.
191          *
192          * This property is {@code false} by default.
193          * @param autoEnterEnabled {@code true} if the system will automatically put the activity
194          *                                     in picture-in-picture mode.
195          *
196          * @return this builder instance.
197          */
198         @NonNull
setAutoEnterEnabled(boolean autoEnterEnabled)199         public Builder setAutoEnterEnabled(boolean autoEnterEnabled) {
200             mAutoEnterEnabled = autoEnterEnabled;
201             return this;
202         }
203 
204         /**
205          * Sets whether the system can seamlessly resize the window while the activity is in
206          * picture-in-picture mode. This should normally be the case for video content and
207          * when it's set to {@code false}, system will perform transitions to overcome the
208          * artifacts due to resize.
209          *
210          * This property is {@code true} by default for backwards compatibility.
211          * @param seamlessResizeEnabled {@code true} if the system can seamlessly resize the window
212          *                                          while activity is in picture-in-picture mode.
213          * @return this builder instance.
214          */
215         @NonNull
setSeamlessResizeEnabled(boolean seamlessResizeEnabled)216         public Builder setSeamlessResizeEnabled(boolean seamlessResizeEnabled) {
217             mSeamlessResizeEnabled = seamlessResizeEnabled;
218             return this;
219         }
220 
221         /**
222          * Sets a title for the picture-in-picture window, which may be displayed by the system to
223          * give the user information about what this PIP is generally being used for.
224          *
225          * @param title General information about the PIP content
226          * @return this builder instance.
227          */
228         @NonNull
setTitle(@ullable CharSequence title)229         public Builder setTitle(@Nullable CharSequence title) {
230             mTitle = title;
231             return this;
232         }
233 
234         /**
235          * Sets a subtitle for the picture-in-picture window, which may be displayed by the system
236          * to give the user more detailed information about what this PIP is displaying.<br/>
237          *
238          * Setting a title via {@link PictureInPictureParams.Builder#setTitle(CharSequence)} should
239          * be prioritized.
240          *
241          * @param subtitle Details about the PIP content.
242          * @return this builder instance
243          */
244         @NonNull
setSubtitle(@ullable CharSequence subtitle)245         public Builder setSubtitle(@Nullable CharSequence subtitle) {
246             mSubtitle = subtitle;
247             return this;
248         }
249 
250         /**
251          * Sets whether the built {@link PictureInPictureParams} represents a launch into
252          * picture-in-picture request.
253          *
254          * This property is {@code false} by default.
255          * @param isLaunchIntoPip {@code true} if the built instance represents a launch into
256          *                                 picture-in-picture request
257          * @return this builder instance.
258          */
259         @NonNull
setIsLaunchIntoPip(boolean isLaunchIntoPip)260         Builder setIsLaunchIntoPip(boolean isLaunchIntoPip) {
261             mIsLaunchIntoPip = isLaunchIntoPip;
262             return this;
263         }
264 
265         /**
266          * @return an immutable {@link PictureInPictureParams} to be used when entering or updating
267          * the activity in picture-in-picture.
268          *
269          * @see Activity#enterPictureInPictureMode(PictureInPictureParams)
270          * @see Activity#setPictureInPictureParams(PictureInPictureParams)
271          */
build()272         public PictureInPictureParams build() {
273             PictureInPictureParams params = new PictureInPictureParams(mAspectRatio,
274                     mExpandedAspectRatio, mUserActions, mCloseAction, mSourceRectHint,
275                     mAutoEnterEnabled, mSeamlessResizeEnabled, mTitle, mSubtitle,
276                     mIsLaunchIntoPip);
277             return params;
278         }
279     }
280 
281     /**
282      * The expected aspect ratio of the picture-in-picture.
283      */
284     @Nullable
285     private Rational mAspectRatio;
286 
287     /**
288      * The expected aspect ratio of the expanded picture-in-picture window.
289      */
290     @Nullable
291     private Rational mExpandedAspectRatio;
292 
293     /**
294      * The set of actions that are associated with this activity when in picture-in-picture.
295      */
296     @Nullable
297     private List<RemoteAction> mUserActions;
298 
299     /**
300      * Action to replace the system close action.
301      */
302     @Nullable
303     private RemoteAction mCloseAction;
304 
305     /**
306      * The source bounds hint used when entering picture-in-picture, relative to the window bounds.
307      * We can use this internally for the transition into picture-in-picture to ensure that a
308      * particular source rect is visible throughout the whole transition.
309      */
310     @Nullable
311     private Rect mSourceRectHint;
312 
313     /**
314      * Whether the system is allowed to automatically put the activity in picture-in-picture mode.
315      * {@link #isAutoEnterEnabled()} defaults to {@code false} if this is not set.
316      */
317     private Boolean mAutoEnterEnabled;
318 
319     /**
320      * Whether system can seamlessly resize the window when activity is in picture-in-picture mode.
321      * {@link #isSeamlessResizeEnabled()} defaults to {@code true} if this is not set for
322      * backwards compatibility.
323      */
324     private Boolean mSeamlessResizeEnabled;
325 
326     /**
327      * Title of the picture-in-picture window to be displayed to the user.
328      */
329     @Nullable
330     private CharSequence mTitle;
331 
332     /**
333      * Subtitle for the picture-in-picture window to be displayed to the user.
334      */
335     @Nullable
336     private CharSequence mSubtitle;
337 
338     /**
339      * Whether this {@link PictureInPictureParams} represents a launch into
340      * picture-in-picture request.
341      * {@link #isLaunchIntoPip()} defaults to {@code false} is this is not set.
342      */
343     private Boolean mIsLaunchIntoPip;
344 
345     /** {@hide} */
PictureInPictureParams()346     PictureInPictureParams() {
347     }
348 
349     /** {@hide} */
PictureInPictureParams(Parcel in)350     PictureInPictureParams(Parcel in) {
351         mAspectRatio = readRationalFromParcel(in);
352         mExpandedAspectRatio = readRationalFromParcel(in);
353         if (in.readInt() != 0) {
354             mUserActions = new ArrayList<>();
355             in.readTypedList(mUserActions, RemoteAction.CREATOR);
356         }
357         mCloseAction = in.readTypedObject(RemoteAction.CREATOR);
358         if (in.readInt() != 0) {
359             mSourceRectHint = Rect.CREATOR.createFromParcel(in);
360         }
361         if (in.readInt() != 0) {
362             mAutoEnterEnabled = in.readBoolean();
363         }
364         if (in.readInt() != 0) {
365             mSeamlessResizeEnabled = in.readBoolean();
366         }
367         if (in.readInt() != 0) {
368             mTitle = in.readCharSequence();
369         }
370         if (in.readInt() != 0) {
371             mSubtitle = in.readCharSequence();
372         }
373         if (in.readInt() != 0) {
374             mIsLaunchIntoPip = in.readBoolean();
375         }
376     }
377 
378     /** {@hide} */
PictureInPictureParams(Rational aspectRatio, Rational expandedAspectRatio, List<RemoteAction> actions, RemoteAction closeAction, Rect sourceRectHint, Boolean autoEnterEnabled, Boolean seamlessResizeEnabled, CharSequence title, CharSequence subtitle, Boolean isLaunchIntoPip)379     PictureInPictureParams(Rational aspectRatio, Rational expandedAspectRatio,
380             List<RemoteAction> actions, RemoteAction closeAction, Rect sourceRectHint,
381             Boolean autoEnterEnabled, Boolean seamlessResizeEnabled, CharSequence title,
382             CharSequence subtitle, Boolean isLaunchIntoPip) {
383         mAspectRatio = aspectRatio;
384         mExpandedAspectRatio = expandedAspectRatio;
385         mUserActions = actions;
386         mCloseAction = closeAction;
387         mSourceRectHint = sourceRectHint;
388         mAutoEnterEnabled = autoEnterEnabled;
389         mSeamlessResizeEnabled = seamlessResizeEnabled;
390         mTitle = title;
391         mSubtitle = subtitle;
392         mIsLaunchIntoPip = isLaunchIntoPip;
393     }
394 
395     /**
396      * Makes a copy from the other picture-in-picture args.
397      * @hide
398      */
PictureInPictureParams(PictureInPictureParams other)399     public PictureInPictureParams(PictureInPictureParams other) {
400         this(other.mAspectRatio, other.mExpandedAspectRatio, other.mUserActions, other.mCloseAction,
401                 other.hasSourceBoundsHint() ? new Rect(other.getSourceRectHint()) : null,
402                 other.mAutoEnterEnabled, other.mSeamlessResizeEnabled,
403                 other.mTitle, other.mSubtitle, other.mIsLaunchIntoPip);
404     }
405 
406     /**
407      * Copies the set parameters from the other picture-in-picture args.
408      * @hide
409      */
copyOnlySet(PictureInPictureParams otherArgs)410     public void copyOnlySet(PictureInPictureParams otherArgs) {
411         if (otherArgs.hasSetAspectRatio()) {
412             mAspectRatio = otherArgs.mAspectRatio;
413         }
414 
415         // Copy either way because null can be used to explicitly unset the value
416         mExpandedAspectRatio = otherArgs.mExpandedAspectRatio;
417 
418         if (otherArgs.hasSetActions()) {
419             mUserActions = otherArgs.mUserActions;
420         }
421         if (otherArgs.hasSetCloseAction()) {
422             mCloseAction = otherArgs.mCloseAction;
423         }
424         if (otherArgs.hasSourceBoundsHint()) {
425             mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
426         }
427         if (otherArgs.mAutoEnterEnabled != null) {
428             mAutoEnterEnabled = otherArgs.mAutoEnterEnabled;
429         }
430         if (otherArgs.mSeamlessResizeEnabled != null) {
431             mSeamlessResizeEnabled = otherArgs.mSeamlessResizeEnabled;
432         }
433         if (otherArgs.hasSetTitle()) {
434             mTitle = otherArgs.mTitle;
435         }
436         if (otherArgs.hasSetSubtitle()) {
437             mSubtitle = otherArgs.mSubtitle;
438         }
439         if (otherArgs.mIsLaunchIntoPip != null) {
440             mIsLaunchIntoPip = otherArgs.mIsLaunchIntoPip;
441         }
442     }
443 
444     /**
445      * @return the aspect ratio. If none is set, return 0.
446      * @hide
447      */
448     @TestApi
getAspectRatioFloat()449     public float getAspectRatioFloat() {
450         if (mAspectRatio != null) {
451             return mAspectRatio.floatValue();
452         }
453         return 0f;
454     }
455 
456     /**
457      * Returns the expected aspect ratio of the picture-in-picture window.
458      *
459      * @return aspect ratio as the desired width / height or {@code null} if not set.
460      * @see PictureInPictureParams.Builder#setAspectRatio(Rational)
461      */
462     @Nullable
getAspectRatio()463     public Rational getAspectRatio() {
464         return mAspectRatio;
465     }
466 
467     /**
468      * @return whether the aspect ratio is set.
469      * @hide
470      */
hasSetAspectRatio()471     public boolean hasSetAspectRatio() {
472         return mAspectRatio != null;
473     }
474 
475     /**
476      * @return the expanded aspect ratio. If none is set, return 0.
477      * @hide
478      */
479     @TestApi
getExpandedAspectRatioFloat()480     public float getExpandedAspectRatioFloat() {
481         if (mExpandedAspectRatio != null) {
482             return mExpandedAspectRatio.floatValue();
483         }
484         return 0f;
485     }
486 
487     /**
488      * Returns the desired aspect ratio of the expanded picture-in-picture window.
489      *
490      * @return aspect ratio as the desired width / height or {@code null} if not set.
491      * @see PictureInPictureParams.Builder#setExpandedAspectRatio(Rational)
492      */
493     @Nullable
getExpandedAspectRatio()494     public Rational getExpandedAspectRatio() {
495         return mExpandedAspectRatio;
496     }
497 
498     /**
499      * @return whether the expanded aspect ratio is set
500      * @hide
501      */
hasSetExpandedAspectRatio()502     public boolean hasSetExpandedAspectRatio() {
503         return mExpandedAspectRatio != null;
504     }
505 
506     /**
507      * Returns the list of user actions that are associated with the activity when in
508      * picture-in-picture mode.
509      *
510      * @return the user actions in a new list.
511      * @see PictureInPictureParams.Builder#setActions(List)
512      */
513     @NonNull
getActions()514     public List<RemoteAction> getActions() {
515         if (mUserActions == null) {
516             return new ArrayList<>();
517         }
518         return mUserActions;
519     }
520 
521     /**
522      * @return whether the user actions are set.
523      * @hide
524      */
hasSetActions()525     public boolean hasSetActions() {
526         return mUserActions != null;
527     }
528 
529     /**
530      * Returns the action that is to replace the system close action.
531      *
532      * @return the close action or {@code null} if not set.
533      * @see PictureInPictureParams.Builder#setCloseAction(RemoteAction)
534      */
535     @Nullable
getCloseAction()536     public RemoteAction getCloseAction() {
537         return mCloseAction;
538     }
539 
540     /**
541      * @return whether the close action was set.
542      * @hide
543      */
hasSetCloseAction()544     public boolean hasSetCloseAction() {
545         return mCloseAction != null;
546     }
547 
548     /**
549      * Truncates the set of actions to the given {@param size}.
550      *
551      * @hide
552      */
truncateActions(int size)553     public void truncateActions(int size) {
554         if (hasSetActions()) {
555             mUserActions = mUserActions.subList(0, Math.min(mUserActions.size(), size));
556         }
557     }
558 
559     /**
560      * Returns the source rect hint.
561      *
562      * @return the source rect hint also known as launch bounds or {@code null} if not set.
563      * @see PictureInPictureParams.Builder#setSourceRectHint(Rect)
564      */
565     @Nullable
getSourceRectHint()566     public Rect getSourceRectHint() {
567         return mSourceRectHint;
568     }
569 
570     /**
571      * @return whether there are launch bounds set
572      * @hide
573      */
hasSourceBoundsHint()574     public boolean hasSourceBoundsHint() {
575         return mSourceRectHint != null && !mSourceRectHint.isEmpty();
576     }
577 
578     /**
579      * Returns whether auto enter picture-in-picture is enabled.
580      *
581      * @return {@code true} if the system will automatically put the activity in
582      * picture-in-picture mode.
583      * @see PictureInPictureParams.Builder#setAutoEnterEnabled(boolean)
584      */
isAutoEnterEnabled()585     public boolean isAutoEnterEnabled() {
586         return mAutoEnterEnabled == null ? false : mAutoEnterEnabled;
587     }
588 
589     /**
590      * Returns whether seamless resize is enabled.
591      *
592      * @return true if the system can seamlessly resize the window while activity is in
593      * picture-in-picture mode.
594      * @see PictureInPictureParams.Builder#setSeamlessResizeEnabled(boolean)
595      */
isSeamlessResizeEnabled()596     public boolean isSeamlessResizeEnabled() {
597         return mSeamlessResizeEnabled == null ? true : mSeamlessResizeEnabled;
598     }
599 
600     /**
601      * @return whether a title was set.
602      * @hide
603      */
hasSetTitle()604     public boolean hasSetTitle() {
605         return mTitle != null;
606     }
607 
608     /**
609      * Returns the title of the picture-in-picture window that may be displayed to the user.
610      *
611      * @return title of the picture-in-picture window.
612      * @see PictureInPictureParams.Builder#setTitle(CharSequence)
613      */
614     @Nullable
getTitle()615     public CharSequence getTitle() {
616         return mTitle;
617     }
618 
619     /**
620      * @return whether a subtitle was set.
621      * @hide
622      */
hasSetSubtitle()623     public boolean hasSetSubtitle() {
624         return mSubtitle != null;
625     }
626 
627     /**
628      * Returns the subtitle of the picture-in-picture window that may be displayed to the user.
629      *
630      * @return subtitle of the picture-in-picture window.
631      * @see PictureInPictureParams.Builder#setSubtitle(CharSequence)
632      */
633     @Nullable
getSubtitle()634     public CharSequence getSubtitle() {
635         return mSubtitle;
636     }
637 
638     /**
639      * @return whether this {@link PictureInPictureParams} represents a launch into pip request.
640      * @hide
641      */
isLaunchIntoPip()642     public boolean isLaunchIntoPip() {
643         return mIsLaunchIntoPip == null ? false : mIsLaunchIntoPip;
644     }
645 
646     /**
647      * @return True if no parameters are set
648      * @hide
649      */
empty()650     public boolean empty() {
651         return !hasSourceBoundsHint() && !hasSetActions() && !hasSetCloseAction()
652                 && !hasSetAspectRatio() && !hasSetExpandedAspectRatio() && mAutoEnterEnabled == null
653                 && mSeamlessResizeEnabled == null && !hasSetTitle()
654                 && !hasSetSubtitle() && mIsLaunchIntoPip == null;
655     }
656 
657     @Override
equals(Object o)658     public boolean equals(Object o) {
659         if (this == o) return true;
660         if (!(o instanceof PictureInPictureParams)) return false;
661         PictureInPictureParams that = (PictureInPictureParams) o;
662         return Objects.equals(mAutoEnterEnabled, that.mAutoEnterEnabled)
663                 && Objects.equals(mSeamlessResizeEnabled, that.mSeamlessResizeEnabled)
664                 && Objects.equals(mAspectRatio, that.mAspectRatio)
665                 && Objects.equals(mExpandedAspectRatio, that.mExpandedAspectRatio)
666                 && Objects.equals(mUserActions, that.mUserActions)
667                 && Objects.equals(mCloseAction, that.mCloseAction)
668                 && Objects.equals(mSourceRectHint, that.mSourceRectHint)
669                 && Objects.equals(mTitle, that.mTitle)
670                 && Objects.equals(mSubtitle, that.mSubtitle)
671                 && Objects.equals(mIsLaunchIntoPip, that.mIsLaunchIntoPip);
672     }
673 
674     @Override
hashCode()675     public int hashCode() {
676         return Objects.hash(mAspectRatio, mExpandedAspectRatio, mUserActions, mCloseAction,
677                 mSourceRectHint, mAutoEnterEnabled, mSeamlessResizeEnabled, mTitle, mSubtitle,
678                 mIsLaunchIntoPip);
679     }
680 
681     @Override
describeContents()682     public int describeContents() {
683         return 0;
684     }
685 
686     @Override
writeToParcel(Parcel out, int flags)687     public void writeToParcel(Parcel out, int flags) {
688         writeRationalToParcel(mAspectRatio, out);
689         writeRationalToParcel(mExpandedAspectRatio, out);
690         if (mUserActions != null) {
691             out.writeInt(1);
692             out.writeTypedList(mUserActions, 0);
693         } else {
694             out.writeInt(0);
695         }
696 
697         out.writeTypedObject(mCloseAction, 0);
698 
699         if (mSourceRectHint != null) {
700             out.writeInt(1);
701             mSourceRectHint.writeToParcel(out, 0);
702         } else {
703             out.writeInt(0);
704         }
705         if (mAutoEnterEnabled != null) {
706             out.writeInt(1);
707             out.writeBoolean(mAutoEnterEnabled);
708         } else {
709             out.writeInt(0);
710         }
711         if (mSeamlessResizeEnabled != null) {
712             out.writeInt(1);
713             out.writeBoolean(mSeamlessResizeEnabled);
714         } else {
715             out.writeInt(0);
716         }
717         if (mTitle != null) {
718             out.writeInt(1);
719             out.writeCharSequence(mTitle);
720         } else {
721             out.writeInt(0);
722         }
723         if (mSubtitle != null) {
724             out.writeInt(1);
725             out.writeCharSequence(mSubtitle);
726         } else {
727             out.writeInt(0);
728         }
729         if (mIsLaunchIntoPip != null) {
730             out.writeInt(1);
731             out.writeBoolean(mIsLaunchIntoPip);
732         } else {
733             out.writeInt(0);
734         }
735     }
736 
writeRationalToParcel(Rational rational, Parcel out)737     private void writeRationalToParcel(Rational rational, Parcel out) {
738         if (rational != null) {
739             out.writeInt(1);
740             out.writeInt(rational.getNumerator());
741             out.writeInt(rational.getDenominator());
742         } else {
743             out.writeInt(0);
744         }
745     }
746 
readRationalFromParcel(Parcel in)747     private Rational readRationalFromParcel(Parcel in) {
748         if (in.readInt() != 0) {
749             return new Rational(in.readInt(), in.readInt());
750         }
751         return null;
752     }
753 
754     @Override
toString()755     public String toString() {
756         return "PictureInPictureParams("
757                 + " aspectRatio=" + getAspectRatio()
758                 + " expandedAspectRatio=" + mExpandedAspectRatio
759                 + " sourceRectHint=" + getSourceRectHint()
760                 + " hasSetActions=" + hasSetActions()
761                 + " hasSetCloseAction=" + hasSetCloseAction()
762                 + " isAutoPipEnabled=" + isAutoEnterEnabled()
763                 + " isSeamlessResizeEnabled=" + isSeamlessResizeEnabled()
764                 + " title=" + getTitle()
765                 + " subtitle=" + getSubtitle()
766                 + " isLaunchIntoPip=" + isLaunchIntoPip()
767                 + ")";
768     }
769 
770     public static final @android.annotation.NonNull Creator<PictureInPictureParams> CREATOR =
771             new Creator<PictureInPictureParams>() {
772                 public PictureInPictureParams createFromParcel(Parcel in) {
773                     return new PictureInPictureParams(in);
774                 }
775                 public PictureInPictureParams[] newArray(int size) {
776                     return new PictureInPictureParams[size];
777                 }
778             };
779 }
780