1/*
2 * Copyright (C) 2012 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.support.v4.app;
18
19import android.app.Activity;
20import android.app.ActivityOptions;
21import android.app.PendingIntent;
22import android.content.Context;
23import android.graphics.Bitmap;
24import android.graphics.Rect;
25import android.os.Build;
26import android.os.Bundle;
27import android.support.annotation.Nullable;
28import android.support.annotation.RequiresApi;
29import android.support.v4.util.Pair;
30import android.view.View;
31
32/**
33 * Helper for accessing features in {@link android.app.ActivityOptions} in a backwards compatible
34 * fashion.
35 */
36public class ActivityOptionsCompat {
37    /**
38     * A long in the extras delivered by {@link #requestUsageTimeReport} that contains
39     * the total time (in ms) the user spent in the app flow.
40     */
41    public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
42
43    /**
44     * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains
45     * detailed information about the time spent in each package associated with the app;
46     * each key is a package name, whose value is a long containing the time (in ms).
47     */
48    public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
49
50    /**
51     * Create an ActivityOptions specifying a custom animation to run when the
52     * activity is displayed.
53     *
54     * @param context Who is defining this. This is the application that the
55     * animation resources will be loaded from.
56     * @param enterResId A resource ID of the animation resource to use for the
57     * incoming activity. Use 0 for no animation.
58     * @param exitResId A resource ID of the animation resource to use for the
59     * outgoing activity. Use 0 for no animation.
60     * @return Returns a new ActivityOptions object that you can use to supply
61     * these options as the options Bundle when starting an activity.
62     */
63    public static ActivityOptionsCompat makeCustomAnimation(Context context,
64            int enterResId, int exitResId) {
65        if (Build.VERSION.SDK_INT >= 16) {
66            return createImpl(ActivityOptions.makeCustomAnimation(context, enterResId, exitResId));
67        }
68        return new ActivityOptionsCompat();
69    }
70
71    /**
72     * Create an ActivityOptions specifying an animation where the new activity is
73     * scaled from a small originating area of the screen to its final full
74     * representation.
75     * <p/>
76     * If the Intent this is being used with has not set its
77     * {@link android.content.Intent#setSourceBounds(android.graphics.Rect)},
78     * those bounds will be filled in for you based on the initial bounds passed
79     * in here.
80     *
81     * @param source The View that the new activity is animating from. This
82     * defines the coordinate space for startX and startY.
83     * @param startX The x starting location of the new activity, relative to
84     * source.
85     * @param startY The y starting location of the activity, relative to source.
86     * @param startWidth The initial width of the new activity.
87     * @param startHeight The initial height of the new activity.
88     * @return Returns a new ActivityOptions object that you can use to supply
89     * these options as the options Bundle when starting an activity.
90     */
91    public static ActivityOptionsCompat makeScaleUpAnimation(View source,
92            int startX, int startY, int startWidth, int startHeight) {
93        if (Build.VERSION.SDK_INT >= 16) {
94            return createImpl(ActivityOptions.makeScaleUpAnimation(
95                    source, startX, startY, startWidth, startHeight));
96        }
97        return new ActivityOptionsCompat();
98    }
99
100    /**
101     * Create an ActivityOptions specifying an animation where the new
102     * activity is revealed from a small originating area of the screen to
103     * its final full representation.
104     *
105     * @param source The View that the new activity is animating from.  This
106     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
107     * @param startX The x starting location of the new activity, relative to <var>source</var>.
108     * @param startY The y starting location of the activity, relative to <var>source</var>.
109     * @param width The initial width of the new activity.
110     * @param height The initial height of the new activity.
111     * @return Returns a new ActivityOptions object that you can use to
112     * supply these options as the options Bundle when starting an activity.
113     */
114    public static ActivityOptionsCompat makeClipRevealAnimation(View source,
115            int startX, int startY, int width, int height) {
116        if (Build.VERSION.SDK_INT >= 23) {
117            return createImpl(ActivityOptions.makeClipRevealAnimation(
118                    source, startX, startY, width, height));
119        }
120        return new ActivityOptionsCompat();
121    }
122
123    /**
124     * Create an ActivityOptions specifying an animation where a thumbnail is
125     * scaled from a given position to the new activity window that is being
126     * started.
127     * <p/>
128     * If the Intent this is being used with has not set its
129     * {@link android.content.Intent#setSourceBounds(android.graphics.Rect)},
130     * those bounds will be filled in for you based on the initial thumbnail
131     * location and size provided here.
132     *
133     * @param source The View that this thumbnail is animating from. This
134     * defines the coordinate space for startX and startY.
135     * @param thumbnail The bitmap that will be shown as the initial thumbnail
136     * of the animation.
137     * @param startX The x starting location of the bitmap, relative to source.
138     * @param startY The y starting location of the bitmap, relative to source.
139     * @return Returns a new ActivityOptions object that you can use to supply
140     * these options as the options Bundle when starting an activity.
141     */
142    public static ActivityOptionsCompat makeThumbnailScaleUpAnimation(View source,
143            Bitmap thumbnail, int startX, int startY) {
144        if (Build.VERSION.SDK_INT >= 16) {
145            return createImpl(ActivityOptions.makeThumbnailScaleUpAnimation(
146                    source, thumbnail, startX, startY));
147        }
148        return new ActivityOptionsCompat();
149    }
150
151    /**
152     * Create an ActivityOptions to transition between Activities using cross-Activity scene
153     * animations. This method carries the position of one shared element to the started Activity.
154     * The position of <code>sharedElement</code> will be used as the epicenter for the
155     * exit Transition. The position of the shared element in the launched Activity will be the
156     * epicenter of its entering Transition.
157     *
158     * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
159     * enabled on the calling Activity to cause an exit transition. The same must be in
160     * the called Activity to get an entering transition.</p>
161     * @param activity The Activity whose window contains the shared elements.
162     * @param sharedElement The View to transition to the started Activity. sharedElement must
163     *                      have a non-null sharedElementName.
164     * @param sharedElementName The shared element name as used in the target Activity. This may
165     *                          be null if it has the same name as sharedElement.
166     * @return Returns a new ActivityOptions object that you can use to
167     *         supply these options as the options Bundle when starting an activity.
168     */
169    public static ActivityOptionsCompat makeSceneTransitionAnimation(Activity activity,
170            View sharedElement, String sharedElementName) {
171        if (Build.VERSION.SDK_INT >= 21) {
172            return createImpl(ActivityOptions.makeSceneTransitionAnimation(
173                    activity, sharedElement, sharedElementName));
174        }
175        return new ActivityOptionsCompat();
176    }
177
178    /**
179     * Create an ActivityOptions to transition between Activities using cross-Activity scene
180     * animations. This method carries the position of multiple shared elements to the started
181     * Activity. The position of the first element in sharedElements
182     * will be used as the epicenter for the exit Transition. The position of the associated
183     * shared element in the launched Activity will be the epicenter of its entering Transition.
184     *
185     * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
186     * enabled on the calling Activity to cause an exit transition. The same must be in
187     * the called Activity to get an entering transition.</p>
188     * @param activity The Activity whose window contains the shared elements.
189     * @param sharedElements The names of the shared elements to transfer to the called
190     *                       Activity and their associated Views. The Views must each have
191     *                       a unique shared element name.
192     * @return Returns a new ActivityOptions object that you can use to
193     *         supply these options as the options Bundle when starting an activity.
194     */
195    @SuppressWarnings("unchecked")
196    public static ActivityOptionsCompat makeSceneTransitionAnimation(Activity activity,
197            Pair<View, String>... sharedElements) {
198        if (Build.VERSION.SDK_INT >= 21) {
199            android.util.Pair<View, String>[] pairs = null;
200            if (sharedElements != null) {
201                pairs = new android.util.Pair[sharedElements.length];
202                for (int i = 0; i < sharedElements.length; i++) {
203                    pairs[i] = android.util.Pair.create(
204                            sharedElements[i].first, sharedElements[i].second);
205                }
206            }
207            return createImpl(ActivityOptions.makeSceneTransitionAnimation(activity, pairs));
208        }
209        return new ActivityOptionsCompat();
210    }
211
212    /**
213     * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be
214     * presented to the user but will instead be only available through the recents task list.
215     * In addition, the new task wil be affiliated with the launching activity's task.
216     * Affiliated tasks are grouped together in the recents task list.
217     *
218     * <p>This behavior is not supported for activities with
219     * {@link android.R.attr#launchMode launchMode} values of
220     * <code>singleInstance</code> or <code>singleTask</code>.
221     */
222    public static ActivityOptionsCompat makeTaskLaunchBehind() {
223        if (Build.VERSION.SDK_INT >= 21) {
224            return createImpl(ActivityOptions.makeTaskLaunchBehind());
225        }
226        return new ActivityOptionsCompat();
227    }
228
229    /**
230     * Create a basic ActivityOptions that has no special animation associated with it.
231     * Other options can still be set.
232     */
233    public static ActivityOptionsCompat makeBasic() {
234        if (Build.VERSION.SDK_INT >= 23) {
235            return createImpl(ActivityOptions.makeBasic());
236        }
237        return new ActivityOptionsCompat();
238    }
239
240    @RequiresApi(16)
241    private static ActivityOptionsCompat createImpl(ActivityOptions options) {
242        if (Build.VERSION.SDK_INT >= 24) {
243            return new ActivityOptionsCompatApi24Impl(options);
244        } else if (Build.VERSION.SDK_INT >= 23) {
245            return new ActivityOptionsCompatApi23Impl(options);
246        } else {
247            return new ActivityOptionsCompatApi16Impl(options);
248        }
249    }
250
251    @RequiresApi(16)
252    private static class ActivityOptionsCompatApi16Impl extends ActivityOptionsCompat {
253        protected final ActivityOptions mActivityOptions;
254
255        ActivityOptionsCompatApi16Impl(ActivityOptions activityOptions) {
256            mActivityOptions = activityOptions;
257        }
258
259        @Override
260        public Bundle toBundle() {
261            return mActivityOptions.toBundle();
262        }
263
264        @Override
265        public void update(ActivityOptionsCompat otherOptions) {
266            if (otherOptions instanceof ActivityOptionsCompatApi16Impl) {
267                ActivityOptionsCompatApi16Impl otherImpl =
268                        (ActivityOptionsCompatApi16Impl) otherOptions;
269                mActivityOptions.update(otherImpl.mActivityOptions);
270            }
271        }
272    }
273
274    @RequiresApi(23)
275    private static class ActivityOptionsCompatApi23Impl extends ActivityOptionsCompatApi16Impl {
276        ActivityOptionsCompatApi23Impl(ActivityOptions activityOptions) {
277            super(activityOptions);
278        }
279
280        @Override
281        public void requestUsageTimeReport(PendingIntent receiver) {
282            mActivityOptions.requestUsageTimeReport(receiver);
283        }
284    }
285
286    @RequiresApi(24)
287    private static class ActivityOptionsCompatApi24Impl extends ActivityOptionsCompatApi23Impl {
288        ActivityOptionsCompatApi24Impl(ActivityOptions activityOptions) {
289            super(activityOptions);
290        }
291
292        @Override
293        public ActivityOptionsCompat setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
294            return new ActivityOptionsCompatApi24Impl(
295                    mActivityOptions.setLaunchBounds(screenSpacePixelRect));
296        }
297
298        @Override
299        public Rect getLaunchBounds() {
300            return mActivityOptions.getLaunchBounds();
301        }
302    }
303
304    protected ActivityOptionsCompat() {
305    }
306
307    /**
308     * Sets the bounds (window size) that the activity should be launched in.
309     * Rect position should be provided in pixels and in screen coordinates.
310     * Set to null explicitly for fullscreen.
311     * <p>
312     * <strong>NOTE:<strong/> This value is ignored on devices that don't have
313     * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or
314     * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled.
315     * @param screenSpacePixelRect Launch bounds to use for the activity or null for fullscreen.
316     */
317    public ActivityOptionsCompat setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
318        return null;
319    }
320
321    /**
322     * Returns the bounds that should be used to launch the activity.
323     * @see #setLaunchBounds(Rect)
324     * @return Bounds used to launch the activity.
325     */
326    @Nullable
327    public Rect getLaunchBounds() {
328        return null;
329    }
330
331    /**
332     * Returns the created options as a Bundle, which can be passed to
333     * {@link android.support.v4.content.ContextCompat#startActivity(Context, android.content.Intent, Bundle)}.
334     * Note that the returned Bundle is still owned by the ActivityOptions
335     * object; you must not modify it, but can supply it to the startActivity
336     * methods that take an options Bundle.
337     */
338    public Bundle toBundle() {
339        return null;
340    }
341
342    /**
343     * Update the current values in this ActivityOptions from those supplied in
344     * otherOptions. Any values defined in otherOptions replace those in the
345     * base options.
346     */
347    public void update(ActivityOptionsCompat otherOptions) {
348        // Do nothing.
349    }
350
351    /**
352     * Ask the the system track that time the user spends in the app being launched, and
353     * report it back once done.  The report will be sent to the given receiver, with
354     * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES}
355     * filled in.
356     *
357     * <p>The time interval tracked is from launching this activity until the user leaves
358     * that activity's flow.  They are considered to stay in the flow as long as
359     * new activities are being launched or returned to from the original flow,
360     * even if this crosses package or task boundaries.  For example, if the originator
361     * starts an activity to view an image, and while there the user selects to share,
362     * which launches their email app in a new task, and they complete the share, the
363     * time during that entire operation will be included until they finally hit back from
364     * the original image viewer activity.</p>
365     *
366     * <p>The user is considered to complete a flow once they switch to another
367     * activity that is not part of the tracked flow.  This may happen, for example, by
368     * using the notification shade, launcher, or recents to launch or switch to another
369     * app.  Simply going in to these navigation elements does not break the flow (although
370     * the launcher and recents stops time tracking of the session); it is the act of
371     * going somewhere else that completes the tracking.</p>
372     *
373     * @param receiver A broadcast receiver that will receive the report.
374     */
375    public void requestUsageTimeReport(PendingIntent receiver) {
376        // Do nothing.
377    }
378}
379