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.Nullable;
20 import android.annotation.TestApi;
21 import android.graphics.Rect;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.util.Rational;
25 
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 /**
30  * Represents a set of parameters used to initialize and update an Activity in picture-in-picture
31  * mode.
32  */
33 public final class PictureInPictureParams implements Parcelable {
34 
35     /**
36      * Builder class for {@link PictureInPictureParams} objects.
37      */
38     public static class Builder {
39 
40         @Nullable
41         private Rational mAspectRatio;
42 
43         @Nullable
44         private List<RemoteAction> mUserActions;
45 
46         @Nullable
47         private Rect mSourceRectHint;
48 
49         /**
50          * Sets the aspect ratio.  This aspect ratio is defined as the desired width / height, and
51          * does not change upon device rotation.
52          *
53          * @param aspectRatio the new aspect ratio for the activity in picture-in-picture, must be
54          * between 2.39:1 and 1:2.39 (inclusive).
55          *
56          * @return this builder instance.
57          */
setAspectRatio(Rational aspectRatio)58         public Builder setAspectRatio(Rational aspectRatio) {
59             mAspectRatio = aspectRatio;
60             return this;
61         }
62 
63         /**
64          * Sets the user actions.  If there are more than
65          * {@link Activity#getMaxNumPictureInPictureActions()} actions, then the input list
66          * will be truncated to that number.
67          *
68          * @param actions the new actions to show in the picture-in-picture menu.
69          *
70          * @return this builder instance.
71          *
72          * @see RemoteAction
73          */
setActions(List<RemoteAction> actions)74         public Builder setActions(List<RemoteAction> actions) {
75             if (mUserActions != null) {
76                 mUserActions = null;
77             }
78             if (actions != null) {
79                 mUserActions = new ArrayList<>(actions);
80             }
81             return this;
82         }
83 
84         /**
85          * Sets the source bounds hint. These bounds are only used when an activity first enters
86          * picture-in-picture, and describe the bounds in window coordinates of activity entering
87          * picture-in-picture that will be visible following the transition. For the best effect,
88          * these bounds should also match the aspect ratio in the arguments.
89          *
90          * @param launchBounds window-coordinate bounds indicating the area of the activity that
91          * will still be visible following the transition into picture-in-picture (eg. the video
92          * view bounds in a video player)
93          *
94          * @return this builder instance.
95          */
setSourceRectHint(Rect launchBounds)96         public Builder setSourceRectHint(Rect launchBounds) {
97             if (launchBounds == null) {
98                 mSourceRectHint = null;
99             } else {
100                 mSourceRectHint = new Rect(launchBounds);
101             }
102             return this;
103         }
104 
105         /**
106          * @return an immutable {@link PictureInPictureParams} to be used when entering or updating
107          * the activity in picture-in-picture.
108          *
109          * @see Activity#enterPictureInPictureMode(PictureInPictureParams)
110          * @see Activity#setPictureInPictureParams(PictureInPictureParams)
111          */
build()112         public PictureInPictureParams build() {
113             PictureInPictureParams params = new PictureInPictureParams(mAspectRatio, mUserActions,
114                     mSourceRectHint);
115             return params;
116         }
117     }
118 
119     /**
120      * The expected aspect ratio of the picture-in-picture.
121      */
122     @Nullable
123     private Rational mAspectRatio;
124 
125     /**
126      * The set of actions that are associated with this activity when in picture-in-picture.
127      */
128     @Nullable
129     private List<RemoteAction> mUserActions;
130 
131     /**
132      * The source bounds hint used when entering picture-in-picture, relative to the window bounds.
133      * We can use this internally for the transition into picture-in-picture to ensure that a
134      * particular source rect is visible throughout the whole transition.
135      */
136     @Nullable
137     private Rect mSourceRectHint;
138 
139     /** {@hide} */
PictureInPictureParams()140     PictureInPictureParams() {
141     }
142 
143     /** {@hide} */
PictureInPictureParams(Parcel in)144     PictureInPictureParams(Parcel in) {
145         if (in.readInt() != 0) {
146             mAspectRatio = new Rational(in.readInt(), in.readInt());
147         }
148         if (in.readInt() != 0) {
149             mUserActions = new ArrayList<>();
150             in.readParcelableList(mUserActions, RemoteAction.class.getClassLoader());
151         }
152         if (in.readInt() != 0) {
153             mSourceRectHint = Rect.CREATOR.createFromParcel(in);
154         }
155     }
156 
157     /** {@hide} */
PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions, Rect sourceRectHint)158     PictureInPictureParams(Rational aspectRatio, List<RemoteAction> actions,
159             Rect sourceRectHint) {
160         mAspectRatio = aspectRatio;
161         mUserActions = actions;
162         mSourceRectHint = sourceRectHint;
163     }
164 
165     /**
166      * Copies the set parameters from the other picture-in-picture args.
167      * @hide
168      */
copyOnlySet(PictureInPictureParams otherArgs)169     public void copyOnlySet(PictureInPictureParams otherArgs) {
170         if (otherArgs.hasSetAspectRatio()) {
171             mAspectRatio = otherArgs.mAspectRatio;
172         }
173         if (otherArgs.hasSetActions()) {
174             mUserActions = otherArgs.mUserActions;
175         }
176         if (otherArgs.hasSourceBoundsHint()) {
177             mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
178         }
179     }
180 
181     /**
182      * @return the aspect ratio. If none is set, return 0.
183      * @hide
184      */
185     @TestApi
getAspectRatio()186     public float getAspectRatio() {
187         if (mAspectRatio != null) {
188             return mAspectRatio.floatValue();
189         }
190         return 0f;
191     }
192 
193     /** @hide */
getAspectRatioRational()194     public Rational getAspectRatioRational() {
195         return mAspectRatio;
196     }
197 
198     /**
199      * @return whether the aspect ratio is set.
200      * @hide
201      */
hasSetAspectRatio()202     public boolean hasSetAspectRatio() {
203         return mAspectRatio != null;
204     }
205 
206     /**
207      * @return the set of user actions.
208      * @hide
209      */
210     @TestApi
getActions()211     public List<RemoteAction> getActions() {
212         return mUserActions;
213     }
214 
215     /**
216      * @return whether the user actions are set.
217      * @hide
218      */
hasSetActions()219     public boolean hasSetActions() {
220         return mUserActions != null;
221     }
222 
223     /**
224      * Truncates the set of actions to the given {@param size}.
225      * @hide
226      */
truncateActions(int size)227     public void truncateActions(int size) {
228         if (hasSetActions()) {
229             mUserActions = mUserActions.subList(0, Math.min(mUserActions.size(), size));
230         }
231     }
232 
233     /**
234      * @return the source rect hint
235      * @hide
236      */
237     @TestApi
getSourceRectHint()238     public Rect getSourceRectHint() {
239         return mSourceRectHint;
240     }
241 
242     /**
243      * @return whether there are launch bounds set
244      * @hide
245      */
hasSourceBoundsHint()246     public boolean hasSourceBoundsHint() {
247         return mSourceRectHint != null && !mSourceRectHint.isEmpty();
248     }
249 
250     /**
251      * @return True if no parameters are set
252      * @hide
253      */
empty()254     public boolean empty() {
255         return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio();
256     }
257 
258     @Override
describeContents()259     public int describeContents() {
260         return 0;
261     }
262 
263     @Override
writeToParcel(Parcel out, int flags)264     public void writeToParcel(Parcel out, int flags) {
265         if (mAspectRatio != null) {
266             out.writeInt(1);
267             out.writeInt(mAspectRatio.getNumerator());
268             out.writeInt(mAspectRatio.getDenominator());
269         } else {
270             out.writeInt(0);
271         }
272         if (mUserActions != null) {
273             out.writeInt(1);
274             out.writeParcelableList(mUserActions, 0);
275         } else {
276             out.writeInt(0);
277         }
278         if (mSourceRectHint != null) {
279             out.writeInt(1);
280             mSourceRectHint.writeToParcel(out, 0);
281         } else {
282             out.writeInt(0);
283         }
284     }
285 
286     public static final @android.annotation.NonNull Creator<PictureInPictureParams> CREATOR =
287             new Creator<PictureInPictureParams>() {
288                 public PictureInPictureParams createFromParcel(Parcel in) {
289                     return new PictureInPictureParams(in);
290                 }
291                 public PictureInPictureParams[] newArray(int size) {
292                     return new PictureInPictureParams[size];
293                 }
294             };
295 }
296