• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.TestApi;
22 import android.graphics.Rect;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.util.Rational;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Objects;
30 
31 /**
32  * Represents a set of parameters used to initialize and update an Activity in picture-in-picture
33  * mode.
34  */
35 public final class PictureInPictureParams implements Parcelable {
36 
37     /**
38      * Builder class for {@link PictureInPictureParams} objects.
39      */
40     public static class Builder {
41 
42         @Nullable
43         private Rational mAspectRatio;
44 
45         @Nullable
46         private List<RemoteAction> mUserActions;
47 
48         @Nullable
49         private Rect mSourceRectHint;
50 
51         private Boolean mAutoEnterEnabled;
52 
53         private Boolean mSeamlessResizeEnabled;
54 
55         /**
56          * Sets the aspect ratio.  This aspect ratio is defined as the desired width / height, and
57          * does not change upon device rotation.
58          *
59          * @param aspectRatio the new aspect ratio for the activity in picture-in-picture, must be
60          * between 2.39:1 and 1:2.39 (inclusive).
61          *
62          * @return this builder instance.
63          */
setAspectRatio(Rational aspectRatio)64         public Builder setAspectRatio(Rational aspectRatio) {
65             mAspectRatio = aspectRatio;
66             return this;
67         }
68 
69         /**
70          * Sets the user actions.  If there are more than
71          * {@link Activity#getMaxNumPictureInPictureActions()} actions, then the input list
72          * will be truncated to that number.
73          *
74          * @param actions the new actions to show in the picture-in-picture menu.
75          *
76          * @return this builder instance.
77          *
78          * @see RemoteAction
79          */
setActions(List<RemoteAction> actions)80         public Builder setActions(List<RemoteAction> actions) {
81             if (mUserActions != null) {
82                 mUserActions = null;
83             }
84             if (actions != null) {
85                 mUserActions = new ArrayList<>(actions);
86             }
87             return this;
88         }
89 
90         /**
91          * Sets the source bounds hint. These bounds are only used when an activity first enters
92          * picture-in-picture, and describe the bounds in window coordinates of activity entering
93          * picture-in-picture that will be visible following the transition. For the best effect,
94          * these bounds should also match the aspect ratio in the arguments.
95          *
96          * @param launchBounds window-coordinate bounds indicating the area of the activity that
97          * will still be visible following the transition into picture-in-picture (eg. the video
98          * view bounds in a video player)
99          *
100          * @return this builder instance.
101          */
setSourceRectHint(Rect launchBounds)102         public Builder setSourceRectHint(Rect launchBounds) {
103             if (launchBounds == null) {
104                 mSourceRectHint = null;
105             } else {
106                 mSourceRectHint = new Rect(launchBounds);
107             }
108             return this;
109         }
110 
111         /**
112          * Sets whether the system will automatically put the activity in picture-in-picture mode
113          * without needing/waiting for the activity to call
114          * {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}.
115          *
116          * If true, {@link Activity#onPictureInPictureRequested()} will never be called.
117          *
118          * This property is {@code false} by default.
119          * @param autoEnterEnabled {@code true} if the system will automatically put the activity
120          *                                     in picture-in-picture mode.
121          *
122          * @return this builder instance.
123          */
124         @NonNull
setAutoEnterEnabled(boolean autoEnterEnabled)125         public Builder setAutoEnterEnabled(boolean autoEnterEnabled) {
126             mAutoEnterEnabled = autoEnterEnabled;
127             return this;
128         }
129 
130         /**
131          * Sets whether the system can seamlessly resize the window while the activity is in
132          * picture-in-picture mode. This should normally be the case for video content and
133          * when it's set to {@code false}, system will perform transitions to overcome the
134          * artifacts due to resize.
135          *
136          * This property is {@code true} by default for backwards compatibility.
137          * @param seamlessResizeEnabled {@code true} if the system can seamlessly resize the window
138          *                                          while activity is in picture-in-picture mode.
139          * @return this builder instance.
140          */
141         @NonNull
setSeamlessResizeEnabled(boolean seamlessResizeEnabled)142         public Builder setSeamlessResizeEnabled(boolean seamlessResizeEnabled) {
143             mSeamlessResizeEnabled = seamlessResizeEnabled;
144             return this;
145         }
146 
147         /**
148          * @return an immutable {@link PictureInPictureParams} to be used when entering or updating
149          * the activity in picture-in-picture.
150          *
151          * @see Activity#enterPictureInPictureMode(PictureInPictureParams)
152          * @see Activity#setPictureInPictureParams(PictureInPictureParams)
153          */
build()154         public PictureInPictureParams build() {
155             PictureInPictureParams params = new PictureInPictureParams(mAspectRatio, mUserActions,
156                     mSourceRectHint, mAutoEnterEnabled, mSeamlessResizeEnabled);
157             return params;
158         }
159     }
160 
161     /**
162      * The expected aspect ratio of the picture-in-picture.
163      */
164     @Nullable
165     private Rational mAspectRatio;
166 
167     /**
168      * The set of actions that are associated with this activity when in picture-in-picture.
169      */
170     @Nullable
171     private List<RemoteAction> mUserActions;
172 
173     /**
174      * The source bounds hint used when entering picture-in-picture, relative to the window bounds.
175      * We can use this internally for the transition into picture-in-picture to ensure that a
176      * particular source rect is visible throughout the whole transition.
177      */
178     @Nullable
179     private Rect mSourceRectHint;
180 
181     /**
182      * Whether the system is allowed to automatically put the activity in picture-in-picture mode.
183      * {@link #isAutoEnterEnabled()} defaults to {@code false} if this is not set.
184      */
185     private Boolean mAutoEnterEnabled;
186 
187     /**
188      * Whether system can seamlessly resize the window when activity is in picture-in-picture mode.
189      * {@link #isSeamlessResizeEnabled()} defaults to {@code true} if this is not set for
190      * backwards compatibility.
191      */
192     private Boolean mSeamlessResizeEnabled;
193 
194     /** {@hide} */
PictureInPictureParams()195     PictureInPictureParams() {
196     }
197 
198     /** {@hide} */
PictureInPictureParams(Parcel in)199     PictureInPictureParams(Parcel in) {
200         if (in.readInt() != 0) {
201             mAspectRatio = new Rational(in.readInt(), in.readInt());
202         }
203         if (in.readInt() != 0) {
204             mUserActions = new ArrayList<>();
205             in.readTypedList(mUserActions, RemoteAction.CREATOR);
206         }
207         if (in.readInt() != 0) {
208             mSourceRectHint = Rect.CREATOR.createFromParcel(in);
209         }
210         if (in.readInt() != 0) {
211             mAutoEnterEnabled = in.readBoolean();
212         }
213         if (in.readInt() != 0) {
214             mSeamlessResizeEnabled = in.readBoolean();
215         }
216     }
217 
218     /** {@hide} */
PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions, Rect sourceRectHint, Boolean autoEnterEnabled, Boolean seamlessResizeEnabled)219     PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions,
220             Rect sourceRectHint, Boolean autoEnterEnabled, Boolean seamlessResizeEnabled) {
221         mAspectRatio = aspectRatio;
222         mUserActions = actions;
223         mSourceRectHint = sourceRectHint;
224         mAutoEnterEnabled = autoEnterEnabled;
225         mSeamlessResizeEnabled = seamlessResizeEnabled;
226     }
227 
228     /**
229      * Makes a copy from the other picture-in-picture args.
230      * @hide
231      */
PictureInPictureParams(PictureInPictureParams other)232     public PictureInPictureParams(PictureInPictureParams other) {
233         this(other.mAspectRatio, other.mUserActions,
234                 other.hasSourceBoundsHint() ? new Rect(other.getSourceRectHint()) : null,
235                 other.mAutoEnterEnabled, other.mSeamlessResizeEnabled);
236     }
237 
238     /**
239      * Copies the set parameters from the other picture-in-picture args.
240      * @hide
241      */
copyOnlySet(PictureInPictureParams otherArgs)242     public void copyOnlySet(PictureInPictureParams otherArgs) {
243         if (otherArgs.hasSetAspectRatio()) {
244             mAspectRatio = otherArgs.mAspectRatio;
245         }
246         if (otherArgs.hasSetActions()) {
247             mUserActions = otherArgs.mUserActions;
248         }
249         if (otherArgs.hasSourceBoundsHint()) {
250             mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
251         }
252         if (otherArgs.mAutoEnterEnabled != null) {
253             mAutoEnterEnabled = otherArgs.mAutoEnterEnabled;
254         }
255         if (otherArgs.mSeamlessResizeEnabled != null) {
256             mSeamlessResizeEnabled = otherArgs.mSeamlessResizeEnabled;
257         }
258     }
259 
260     /**
261      * @return the aspect ratio. If none is set, return 0.
262      * @hide
263      */
264     @TestApi
getAspectRatio()265     public float getAspectRatio() {
266         if (mAspectRatio != null) {
267             return mAspectRatio.floatValue();
268         }
269         return 0f;
270     }
271 
272     /** @hide */
getAspectRatioRational()273     public Rational getAspectRatioRational() {
274         return mAspectRatio;
275     }
276 
277     /**
278      * @return whether the aspect ratio is set.
279      * @hide
280      */
hasSetAspectRatio()281     public boolean hasSetAspectRatio() {
282         return mAspectRatio != null;
283     }
284 
285     /**
286      * @return the set of user actions.
287      * @hide
288      */
289     @TestApi
getActions()290     public List<RemoteAction> getActions() {
291         return mUserActions;
292     }
293 
294     /**
295      * @return whether the user actions are set.
296      * @hide
297      */
hasSetActions()298     public boolean hasSetActions() {
299         return mUserActions != null;
300     }
301 
302     /**
303      * Truncates the set of actions to the given {@param size}.
304      * @hide
305      */
truncateActions(int size)306     public void truncateActions(int size) {
307         if (hasSetActions()) {
308             mUserActions = mUserActions.subList(0, Math.min(mUserActions.size(), size));
309         }
310     }
311 
312     /**
313      * @return the source rect hint
314      * @hide
315      */
316     @TestApi
getSourceRectHint()317     public Rect getSourceRectHint() {
318         return mSourceRectHint;
319     }
320 
321     /**
322      * @return whether there are launch bounds set
323      * @hide
324      */
hasSourceBoundsHint()325     public boolean hasSourceBoundsHint() {
326         return mSourceRectHint != null && !mSourceRectHint.isEmpty();
327     }
328 
329     /**
330      * @return whether auto pip is enabled.
331      * @hide
332      */
isAutoEnterEnabled()333     public boolean isAutoEnterEnabled() {
334         return mAutoEnterEnabled == null ? false : mAutoEnterEnabled;
335     }
336 
337     /**
338      * @return whether seamless resize is enabled.
339      * @hide
340      */
341     @TestApi
isSeamlessResizeEnabled()342     public boolean isSeamlessResizeEnabled() {
343         return mSeamlessResizeEnabled == null ? true : mSeamlessResizeEnabled;
344     }
345 
346     /**
347      * @return True if no parameters are set
348      * @hide
349      */
empty()350     public boolean empty() {
351         return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio()
352                 && mAutoEnterEnabled != null && mSeamlessResizeEnabled != null;
353     }
354 
355     @Override
equals(Object o)356     public boolean equals(Object o) {
357         if (this == o) return true;
358         if (!(o instanceof PictureInPictureParams)) return false;
359         PictureInPictureParams that = (PictureInPictureParams) o;
360         return Objects.equals(mAutoEnterEnabled, that.mAutoEnterEnabled)
361                 && Objects.equals(mSeamlessResizeEnabled, that.mSeamlessResizeEnabled)
362                 && Objects.equals(mAspectRatio, that.mAspectRatio)
363                 && Objects.equals(mUserActions, that.mUserActions)
364                 && Objects.equals(mSourceRectHint, that.mSourceRectHint);
365     }
366 
367     @Override
hashCode()368     public int hashCode() {
369         return Objects.hash(mAspectRatio, mUserActions, mSourceRectHint,
370                 mAutoEnterEnabled, mSeamlessResizeEnabled);
371     }
372 
373     @Override
describeContents()374     public int describeContents() {
375         return 0;
376     }
377 
378     @Override
writeToParcel(Parcel out, int flags)379     public void writeToParcel(Parcel out, int flags) {
380         if (mAspectRatio != null) {
381             out.writeInt(1);
382             out.writeInt(mAspectRatio.getNumerator());
383             out.writeInt(mAspectRatio.getDenominator());
384         } else {
385             out.writeInt(0);
386         }
387         if (mUserActions != null) {
388             out.writeInt(1);
389             out.writeTypedList(mUserActions, 0);
390         } else {
391             out.writeInt(0);
392         }
393         if (mSourceRectHint != null) {
394             out.writeInt(1);
395             mSourceRectHint.writeToParcel(out, 0);
396         } else {
397             out.writeInt(0);
398         }
399         if (mAutoEnterEnabled != null) {
400             out.writeInt(1);
401             out.writeBoolean(mAutoEnterEnabled);
402         } else {
403             out.writeInt(0);
404         }
405         if (mSeamlessResizeEnabled != null) {
406             out.writeInt(1);
407             out.writeBoolean(mSeamlessResizeEnabled);
408         } else {
409             out.writeInt(0);
410         }
411     }
412 
413     @Override
toString()414     public String toString() {
415         return "PictureInPictureParams("
416                 + " aspectRatio=" + getAspectRatioRational()
417                 + " sourceRectHint=" + getSourceRectHint()
418                 + " hasSetActions=" + hasSetActions()
419                 + " isAutoPipEnabled=" + isAutoEnterEnabled()
420                 + " isSeamlessResizeEnabled=" + isSeamlessResizeEnabled()
421                 + ")";
422     }
423 
424     public static final @android.annotation.NonNull Creator<PictureInPictureParams> CREATOR =
425             new Creator<PictureInPictureParams>() {
426                 public PictureInPictureParams createFromParcel(Parcel in) {
427                     return new PictureInPictureParams(in);
428                 }
429                 public PictureInPictureParams[] newArray(int size) {
430                     return new PictureInPictureParams[size];
431                 }
432             };
433 }
434