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