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 makeCustomAnimation(@onNull Context context, int enterResId, int exitResId)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 makeScaleUpAnimation(@onNull View source, int startX, int startY, int startWidth, int startHeight)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 makeClipRevealAnimation(@onNull View source, int startX, int startY, int width, int height)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 makeThumbnailScaleUpAnimation(@onNull View source, @NonNull Bitmap thumbnail, int startX, int startY)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 makeSceneTransitionAnimation(@onNull Activity activity, @NonNull View sharedElement, @NonNull String sharedElementName)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") makeSceneTransitionAnimation(@onNull Activity activity, Pair<View, String>... sharedElements)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 makeTaskLaunchBehind()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 makeBasic()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 ActivityOptionsCompatImpl(ActivityOptions activityOptions)256 ActivityOptionsCompatImpl(ActivityOptions activityOptions) { 257 mActivityOptions = activityOptions; 258 } 259 260 @Override toBundle()261 public Bundle toBundle() { 262 return mActivityOptions.toBundle(); 263 } 264 265 @Override update(ActivityOptionsCompat otherOptions)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 requestUsageTimeReport(PendingIntent receiver)275 public void requestUsageTimeReport(PendingIntent receiver) { 276 if (Build.VERSION.SDK_INT >= 23) { 277 mActivityOptions.requestUsageTimeReport(receiver); 278 } 279 } 280 281 @Override setLaunchBounds(@ullable Rect screenSpacePixelRect)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 getLaunchBounds()291 public Rect getLaunchBounds() { 292 if (Build.VERSION.SDK_INT < 24) { 293 return null; 294 } 295 return mActivityOptions.getLaunchBounds(); 296 } 297 } 298 ActivityOptionsCompat()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 setLaunchBounds(@ullable Rect screenSpacePixelRect)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 getLaunchBounds()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 toBundle()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 */ update(@onNull ActivityOptionsCompat otherOptions)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 */ requestUsageTimeReport(@onNull PendingIntent receiver)372 public void requestUsageTimeReport(@NonNull PendingIntent receiver) { 373 // Do nothing. 374 } 375 } 376