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