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 android.app; 18 19 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 20 import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 21 import static android.view.Display.INVALID_DISPLAY; 22 23 import android.annotation.Nullable; 24 import android.annotation.TestApi; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.graphics.Bitmap; 28 import android.graphics.Bitmap.Config; 29 import android.graphics.GraphicBuffer; 30 import android.graphics.Rect; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.IRemoteCallback; 34 import android.os.Parcelable; 35 import android.os.RemoteException; 36 import android.os.ResultReceiver; 37 import android.transition.Transition; 38 import android.transition.TransitionListenerAdapter; 39 import android.transition.TransitionManager; 40 import android.util.Pair; 41 import android.util.Slog; 42 import android.view.AppTransitionAnimationSpec; 43 import android.view.IAppTransitionAnimationSpecsFuture; 44 import android.view.View; 45 import android.view.ViewGroup; 46 import android.view.Window; 47 48 import java.util.ArrayList; 49 50 /** 51 * Helper class for building an options Bundle that can be used with 52 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) 53 * Context.startActivity(Intent, Bundle)} and related methods. 54 */ 55 public class ActivityOptions { 56 private static final String TAG = "ActivityOptions"; 57 58 /** 59 * A long in the extras delivered by {@link #requestUsageTimeReport} that contains 60 * the total time (in ms) the user spent in the app flow. 61 */ 62 public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time"; 63 64 /** 65 * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains 66 * detailed information about the time spent in each package associated with the app; 67 * each key is a package name, whose value is a long containing the time (in ms). 68 */ 69 public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages"; 70 71 /** 72 * The package name that created the options. 73 * @hide 74 */ 75 public static final String KEY_PACKAGE_NAME = "android:activity.packageName"; 76 77 /** 78 * The bounds (window size) that the activity should be launched in. Set to null explicitly for 79 * full screen. If the key is not found, previous bounds will be preserved. 80 * NOTE: This value is ignored on devices that don't have 81 * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or 82 * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled. 83 * @hide 84 */ 85 public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds"; 86 87 /** 88 * Type of animation that arguments specify. 89 * @hide 90 */ 91 public static final String KEY_ANIM_TYPE = "android:activity.animType"; 92 93 /** 94 * Custom enter animation resource ID. 95 * @hide 96 */ 97 public static final String KEY_ANIM_ENTER_RES_ID = "android:activity.animEnterRes"; 98 99 /** 100 * Custom exit animation resource ID. 101 * @hide 102 */ 103 public static final String KEY_ANIM_EXIT_RES_ID = "android:activity.animExitRes"; 104 105 /** 106 * Custom in-place animation resource ID. 107 * @hide 108 */ 109 public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes"; 110 111 /** 112 * Bitmap for thumbnail animation. 113 * @hide 114 */ 115 public static final String KEY_ANIM_THUMBNAIL = "android:activity.animThumbnail"; 116 117 /** 118 * Start X position of thumbnail animation. 119 * @hide 120 */ 121 public static final String KEY_ANIM_START_X = "android:activity.animStartX"; 122 123 /** 124 * Start Y position of thumbnail animation. 125 * @hide 126 */ 127 public static final String KEY_ANIM_START_Y = "android:activity.animStartY"; 128 129 /** 130 * Initial width of the animation. 131 * @hide 132 */ 133 public static final String KEY_ANIM_WIDTH = "android:activity.animWidth"; 134 135 /** 136 * Initial height of the animation. 137 * @hide 138 */ 139 public static final String KEY_ANIM_HEIGHT = "android:activity.animHeight"; 140 141 /** 142 * Callback for when animation is started. 143 * @hide 144 */ 145 public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener"; 146 147 /** 148 * Callback for when the last frame of the animation is played. 149 * @hide 150 */ 151 private static final String KEY_ANIMATION_FINISHED_LISTENER = 152 "android:activity.animationFinishedListener"; 153 154 /** 155 * Descriptions of app transition animations to be played during the activity launch. 156 */ 157 private static final String KEY_ANIM_SPECS = "android:activity.animSpecs"; 158 159 /** 160 * The display id the activity should be launched into. 161 * @see #setLaunchDisplayId(int) 162 * @hide 163 */ 164 private static final String KEY_LAUNCH_DISPLAY_ID = "android.activity.launchDisplayId"; 165 166 /** 167 * The stack id the activity should be launched into. 168 * @hide 169 */ 170 private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId"; 171 172 /** 173 * The task id the activity should be launched into. 174 * @hide 175 */ 176 private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId"; 177 178 /** 179 * See {@link #setTaskOverlay}. 180 * @hide 181 */ 182 private static final String KEY_TASK_OVERLAY = "android.activity.taskOverlay"; 183 184 /** 185 * See {@link #setTaskOverlay}. 186 * @hide 187 */ 188 private static final String KEY_TASK_OVERLAY_CAN_RESUME = 189 "android.activity.taskOverlayCanResume"; 190 191 /** 192 * Where the docked stack should be positioned. 193 * @hide 194 */ 195 private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode"; 196 197 /** 198 * Determines whether to disallow the outgoing activity from entering picture-in-picture as the 199 * result of a new activity being launched. 200 * @hide 201 */ 202 private static final String KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING = 203 "android:activity.disallowEnterPictureInPictureWhileLaunching"; 204 205 /** 206 * For Activity transitions, the calling Activity's TransitionListener used to 207 * notify the called Activity when the shared element and the exit transitions 208 * complete. 209 */ 210 private static final String KEY_TRANSITION_COMPLETE_LISTENER 211 = "android:activity.transitionCompleteListener"; 212 213 private static final String KEY_TRANSITION_IS_RETURNING 214 = "android:activity.transitionIsReturning"; 215 private static final String KEY_TRANSITION_SHARED_ELEMENTS 216 = "android:activity.sharedElementNames"; 217 private static final String KEY_RESULT_DATA = "android:activity.resultData"; 218 private static final String KEY_RESULT_CODE = "android:activity.resultCode"; 219 private static final String KEY_EXIT_COORDINATOR_INDEX 220 = "android:activity.exitCoordinatorIndex"; 221 222 private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport"; 223 private static final String KEY_ROTATION_ANIMATION_HINT = "android:activity.rotationAnimationHint"; 224 225 private static final String KEY_INSTANT_APP_VERIFICATION_BUNDLE 226 = "android:instantapps.installerbundle"; 227 private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture"; 228 229 /** @hide */ 230 public static final int ANIM_NONE = 0; 231 /** @hide */ 232 public static final int ANIM_CUSTOM = 1; 233 /** @hide */ 234 public static final int ANIM_SCALE_UP = 2; 235 /** @hide */ 236 public static final int ANIM_THUMBNAIL_SCALE_UP = 3; 237 /** @hide */ 238 public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4; 239 /** @hide */ 240 public static final int ANIM_SCENE_TRANSITION = 5; 241 /** @hide */ 242 public static final int ANIM_DEFAULT = 6; 243 /** @hide */ 244 public static final int ANIM_LAUNCH_TASK_BEHIND = 7; 245 /** @hide */ 246 public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8; 247 /** @hide */ 248 public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9; 249 /** @hide */ 250 public static final int ANIM_CUSTOM_IN_PLACE = 10; 251 /** @hide */ 252 public static final int ANIM_CLIP_REVEAL = 11; 253 254 private String mPackageName; 255 private Rect mLaunchBounds; 256 private int mAnimationType = ANIM_NONE; 257 private int mCustomEnterResId; 258 private int mCustomExitResId; 259 private int mCustomInPlaceResId; 260 private Bitmap mThumbnail; 261 private int mStartX; 262 private int mStartY; 263 private int mWidth; 264 private int mHeight; 265 private IRemoteCallback mAnimationStartedListener; 266 private IRemoteCallback mAnimationFinishedListener; 267 private ResultReceiver mTransitionReceiver; 268 private boolean mIsReturning; 269 private ArrayList<String> mSharedElementNames; 270 private Intent mResultData; 271 private int mResultCode; 272 private int mExitCoordinatorIndex; 273 private PendingIntent mUsageTimeReport; 274 private int mLaunchDisplayId = INVALID_DISPLAY; 275 private int mLaunchStackId = INVALID_STACK_ID; 276 private int mLaunchTaskId = -1; 277 private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 278 private boolean mDisallowEnterPictureInPictureWhileLaunching; 279 private boolean mTaskOverlay; 280 private boolean mTaskOverlayCanResume; 281 private AppTransitionAnimationSpec mAnimSpecs[]; 282 private int mRotationAnimationHint = -1; 283 private Bundle mAppVerificationBundle; 284 private IAppTransitionAnimationSpecsFuture mSpecsFuture; 285 286 /** 287 * Create an ActivityOptions specifying a custom animation to run when 288 * the activity is displayed. 289 * 290 * @param context Who is defining this. This is the application that the 291 * animation resources will be loaded from. 292 * @param enterResId A resource ID of the animation resource to use for 293 * the incoming activity. Use 0 for no animation. 294 * @param exitResId A resource ID of the animation resource to use for 295 * the outgoing activity. Use 0 for no animation. 296 * @return Returns a new ActivityOptions object that you can use to 297 * supply these options as the options Bundle when starting an activity. 298 */ 299 public static ActivityOptions makeCustomAnimation(Context context, 300 int enterResId, int exitResId) { 301 return makeCustomAnimation(context, enterResId, exitResId, null, null); 302 } 303 304 /** 305 * Create an ActivityOptions specifying a custom animation to run when 306 * the activity is displayed. 307 * 308 * @param context Who is defining this. This is the application that the 309 * animation resources will be loaded from. 310 * @param enterResId A resource ID of the animation resource to use for 311 * the incoming activity. Use 0 for no animation. 312 * @param exitResId A resource ID of the animation resource to use for 313 * the outgoing activity. Use 0 for no animation. 314 * @param handler If <var>listener</var> is non-null this must be a valid 315 * Handler on which to dispatch the callback; otherwise it should be null. 316 * @param listener Optional OnAnimationStartedListener to find out when the 317 * requested animation has started running. If for some reason the animation 318 * is not executed, the callback will happen immediately. 319 * @return Returns a new ActivityOptions object that you can use to 320 * supply these options as the options Bundle when starting an activity. 321 * @hide 322 */ 323 public static ActivityOptions makeCustomAnimation(Context context, 324 int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) { 325 ActivityOptions opts = new ActivityOptions(); 326 opts.mPackageName = context.getPackageName(); 327 opts.mAnimationType = ANIM_CUSTOM; 328 opts.mCustomEnterResId = enterResId; 329 opts.mCustomExitResId = exitResId; 330 opts.setOnAnimationStartedListener(handler, listener); 331 return opts; 332 } 333 334 /** 335 * Creates an ActivityOptions specifying a custom animation to run in place on an existing 336 * activity. 337 * 338 * @param context Who is defining this. This is the application that the 339 * animation resources will be loaded from. 340 * @param animId A resource ID of the animation resource to use for 341 * the incoming activity. 342 * @return Returns a new ActivityOptions object that you can use to 343 * supply these options as the options Bundle when running an in-place animation. 344 * @hide 345 */ 346 public static ActivityOptions makeCustomInPlaceAnimation(Context context, int animId) { 347 if (animId == 0) { 348 throw new RuntimeException("You must specify a valid animation."); 349 } 350 351 ActivityOptions opts = new ActivityOptions(); 352 opts.mPackageName = context.getPackageName(); 353 opts.mAnimationType = ANIM_CUSTOM_IN_PLACE; 354 opts.mCustomInPlaceResId = animId; 355 return opts; 356 } 357 358 private void setOnAnimationStartedListener(final Handler handler, 359 final OnAnimationStartedListener listener) { 360 if (listener != null) { 361 mAnimationStartedListener = new IRemoteCallback.Stub() { 362 @Override 363 public void sendResult(Bundle data) throws RemoteException { 364 handler.post(new Runnable() { 365 @Override public void run() { 366 listener.onAnimationStarted(); 367 } 368 }); 369 } 370 }; 371 } 372 } 373 374 /** 375 * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation} 376 * to find out when the given animation has started running. 377 * @hide 378 */ 379 public interface OnAnimationStartedListener { 380 void onAnimationStarted(); 381 } 382 383 private void setOnAnimationFinishedListener(final Handler handler, 384 final OnAnimationFinishedListener listener) { 385 if (listener != null) { 386 mAnimationFinishedListener = new IRemoteCallback.Stub() { 387 @Override 388 public void sendResult(Bundle data) throws RemoteException { 389 handler.post(new Runnable() { 390 @Override 391 public void run() { 392 listener.onAnimationFinished(); 393 } 394 }); 395 } 396 }; 397 } 398 } 399 400 /** 401 * Callback for use with {@link ActivityOptions#makeThumbnailAspectScaleDownAnimation} 402 * to find out when the given animation has drawn its last frame. 403 * @hide 404 */ 405 public interface OnAnimationFinishedListener { 406 void onAnimationFinished(); 407 } 408 409 /** 410 * Create an ActivityOptions specifying an animation where the new 411 * activity is scaled from a small originating area of the screen to 412 * its final full representation. 413 * 414 * <p>If the Intent this is being used with has not set its 415 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, 416 * those bounds will be filled in for you based on the initial 417 * bounds passed in here. 418 * 419 * @param source The View that the new activity is animating from. This 420 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 421 * @param startX The x starting location of the new activity, relative to <var>source</var>. 422 * @param startY The y starting location of the activity, relative to <var>source</var>. 423 * @param width The initial width of the new activity. 424 * @param height The initial height of the new activity. 425 * @return Returns a new ActivityOptions object that you can use to 426 * supply these options as the options Bundle when starting an activity. 427 */ 428 public static ActivityOptions makeScaleUpAnimation(View source, 429 int startX, int startY, int width, int height) { 430 ActivityOptions opts = new ActivityOptions(); 431 opts.mPackageName = source.getContext().getPackageName(); 432 opts.mAnimationType = ANIM_SCALE_UP; 433 int[] pts = new int[2]; 434 source.getLocationOnScreen(pts); 435 opts.mStartX = pts[0] + startX; 436 opts.mStartY = pts[1] + startY; 437 opts.mWidth = width; 438 opts.mHeight = height; 439 return opts; 440 } 441 442 /** 443 * Create an ActivityOptions specifying an animation where the new 444 * activity is revealed from a small originating area of the screen to 445 * its final full representation. 446 * 447 * @param source The View that the new activity is animating from. This 448 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 449 * @param startX The x starting location of the new activity, relative to <var>source</var>. 450 * @param startY The y starting location of the activity, relative to <var>source</var>. 451 * @param width The initial width of the new activity. 452 * @param height The initial height of the new activity. 453 * @return Returns a new ActivityOptions object that you can use to 454 * supply these options as the options Bundle when starting an activity. 455 */ 456 public static ActivityOptions makeClipRevealAnimation(View source, 457 int startX, int startY, int width, int height) { 458 ActivityOptions opts = new ActivityOptions(); 459 opts.mAnimationType = ANIM_CLIP_REVEAL; 460 int[] pts = new int[2]; 461 source.getLocationOnScreen(pts); 462 opts.mStartX = pts[0] + startX; 463 opts.mStartY = pts[1] + startY; 464 opts.mWidth = width; 465 opts.mHeight = height; 466 return opts; 467 } 468 469 /** 470 * Create an ActivityOptions specifying an animation where a thumbnail 471 * is scaled from a given position to the new activity window that is 472 * being started. 473 * 474 * <p>If the Intent this is being used with has not set its 475 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, 476 * those bounds will be filled in for you based on the initial 477 * thumbnail location and size provided here. 478 * 479 * @param source The View that this thumbnail is animating from. This 480 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 481 * @param thumbnail The bitmap that will be shown as the initial thumbnail 482 * of the animation. 483 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 484 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 485 * @return Returns a new ActivityOptions object that you can use to 486 * supply these options as the options Bundle when starting an activity. 487 */ 488 public static ActivityOptions makeThumbnailScaleUpAnimation(View source, 489 Bitmap thumbnail, int startX, int startY) { 490 return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null); 491 } 492 493 /** 494 * Create an ActivityOptions specifying an animation where a thumbnail 495 * is scaled from a given position to the new activity window that is 496 * being started. 497 * 498 * @param source The View that this thumbnail is animating from. This 499 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 500 * @param thumbnail The bitmap that will be shown as the initial thumbnail 501 * of the animation. 502 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 503 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 504 * @param listener Optional OnAnimationStartedListener to find out when the 505 * requested animation has started running. If for some reason the animation 506 * is not executed, the callback will happen immediately. 507 * @return Returns a new ActivityOptions object that you can use to 508 * supply these options as the options Bundle when starting an activity. 509 */ 510 private static ActivityOptions makeThumbnailScaleUpAnimation(View source, 511 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { 512 return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true); 513 } 514 515 private static ActivityOptions makeThumbnailAnimation(View source, 516 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener, 517 boolean scaleUp) { 518 ActivityOptions opts = new ActivityOptions(); 519 opts.mPackageName = source.getContext().getPackageName(); 520 opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN; 521 opts.mThumbnail = thumbnail; 522 int[] pts = new int[2]; 523 source.getLocationOnScreen(pts); 524 opts.mStartX = pts[0] + startX; 525 opts.mStartY = pts[1] + startY; 526 opts.setOnAnimationStartedListener(source.getHandler(), listener); 527 return opts; 528 } 529 530 /** 531 * Create an ActivityOptions specifying an animation where a list of activity windows and 532 * thumbnails are aspect scaled to/from a new location. 533 * @hide 534 */ 535 public static ActivityOptions makeMultiThumbFutureAspectScaleAnimation(Context context, 536 Handler handler, IAppTransitionAnimationSpecsFuture specsFuture, 537 OnAnimationStartedListener listener, boolean scaleUp) { 538 ActivityOptions opts = new ActivityOptions(); 539 opts.mPackageName = context.getPackageName(); 540 opts.mAnimationType = scaleUp 541 ? ANIM_THUMBNAIL_ASPECT_SCALE_UP 542 : ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; 543 opts.mSpecsFuture = specsFuture; 544 opts.setOnAnimationStartedListener(handler, listener); 545 return opts; 546 } 547 548 /** 549 * Create an ActivityOptions specifying an animation where the new activity 550 * window and a thumbnail is aspect-scaled to a new location. 551 * 552 * @param source The View that this thumbnail is animating to. This 553 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 554 * @param thumbnail The bitmap that will be shown as the final thumbnail 555 * of the animation. 556 * @param startX The x end location of the bitmap, relative to <var>source</var>. 557 * @param startY The y end location of the bitmap, relative to <var>source</var>. 558 * @param handler If <var>listener</var> is non-null this must be a valid 559 * Handler on which to dispatch the callback; otherwise it should be null. 560 * @param listener Optional OnAnimationStartedListener to find out when the 561 * requested animation has started running. If for some reason the animation 562 * is not executed, the callback will happen immediately. 563 * @return Returns a new ActivityOptions object that you can use to 564 * supply these options as the options Bundle when starting an activity. 565 * @hide 566 */ 567 public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source, 568 Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight, 569 Handler handler, OnAnimationStartedListener listener) { 570 return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, 571 targetWidth, targetHeight, handler, listener, false); 572 } 573 574 private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail, 575 int startX, int startY, int targetWidth, int targetHeight, 576 Handler handler, OnAnimationStartedListener listener, boolean scaleUp) { 577 ActivityOptions opts = new ActivityOptions(); 578 opts.mPackageName = source.getContext().getPackageName(); 579 opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP : 580 ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; 581 opts.mThumbnail = thumbnail; 582 int[] pts = new int[2]; 583 source.getLocationOnScreen(pts); 584 opts.mStartX = pts[0] + startX; 585 opts.mStartY = pts[1] + startY; 586 opts.mWidth = targetWidth; 587 opts.mHeight = targetHeight; 588 opts.setOnAnimationStartedListener(handler, listener); 589 return opts; 590 } 591 592 /** @hide */ 593 public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source, 594 AppTransitionAnimationSpec[] specs, Handler handler, 595 OnAnimationStartedListener onAnimationStartedListener, 596 OnAnimationFinishedListener onAnimationFinishedListener) { 597 ActivityOptions opts = new ActivityOptions(); 598 opts.mPackageName = source.getContext().getPackageName(); 599 opts.mAnimationType = ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; 600 opts.mAnimSpecs = specs; 601 opts.setOnAnimationStartedListener(handler, onAnimationStartedListener); 602 opts.setOnAnimationFinishedListener(handler, onAnimationFinishedListener); 603 return opts; 604 } 605 606 /** 607 * Create an ActivityOptions to transition between Activities using cross-Activity scene 608 * animations. This method carries the position of one shared element to the started Activity. 609 * The position of <code>sharedElement</code> will be used as the epicenter for the 610 * exit Transition. The position of the shared element in the launched Activity will be the 611 * epicenter of its entering Transition. 612 * 613 * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be 614 * enabled on the calling Activity to cause an exit transition. The same must be in 615 * the called Activity to get an entering transition.</p> 616 * @param activity The Activity whose window contains the shared elements. 617 * @param sharedElement The View to transition to the started Activity. 618 * @param sharedElementName The shared element name as used in the target Activity. This 619 * must not be null. 620 * @return Returns a new ActivityOptions object that you can use to 621 * supply these options as the options Bundle when starting an activity. 622 * @see android.transition.Transition#setEpicenterCallback( 623 * android.transition.Transition.EpicenterCallback) 624 */ 625 public static ActivityOptions makeSceneTransitionAnimation(Activity activity, 626 View sharedElement, String sharedElementName) { 627 return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName)); 628 } 629 630 /** 631 * Create an ActivityOptions to transition between Activities using cross-Activity scene 632 * animations. This method carries the position of multiple shared elements to the started 633 * Activity. The position of the first element in sharedElements 634 * will be used as the epicenter for the exit Transition. The position of the associated 635 * shared element in the launched Activity will be the epicenter of its entering Transition. 636 * 637 * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be 638 * enabled on the calling Activity to cause an exit transition. The same must be in 639 * the called Activity to get an entering transition.</p> 640 * @param activity The Activity whose window contains the shared elements. 641 * @param sharedElements The names of the shared elements to transfer to the called 642 * Activity and their associated Views. The Views must each have 643 * a unique shared element name. 644 * @return Returns a new ActivityOptions object that you can use to 645 * supply these options as the options Bundle when starting an activity. 646 * @see android.transition.Transition#setEpicenterCallback( 647 * android.transition.Transition.EpicenterCallback) 648 */ 649 @SafeVarargs 650 public static ActivityOptions makeSceneTransitionAnimation(Activity activity, 651 Pair<View, String>... sharedElements) { 652 ActivityOptions opts = new ActivityOptions(); 653 makeSceneTransitionAnimation(activity, activity.getWindow(), opts, 654 activity.mExitTransitionListener, sharedElements); 655 return opts; 656 } 657 658 /** 659 * Call this immediately prior to startActivity to begin a shared element transition 660 * from a non-Activity. The window must support Window.FEATURE_ACTIVITY_TRANSITIONS. 661 * The exit transition will start immediately and the shared element transition will 662 * start once the launched Activity's shared element is ready. 663 * <p> 664 * When all transitions have completed and the shared element has been transfered, 665 * the window's decor View will have its visibility set to View.GONE. 666 * 667 * @hide 668 */ 669 @SafeVarargs 670 public static ActivityOptions startSharedElementAnimation(Window window, 671 Pair<View, String>... sharedElements) { 672 ActivityOptions opts = new ActivityOptions(); 673 final View decorView = window.getDecorView(); 674 if (decorView == null) { 675 return opts; 676 } 677 final ExitTransitionCoordinator exit = 678 makeSceneTransitionAnimation(null, window, opts, null, sharedElements); 679 if (exit != null) { 680 HideWindowListener listener = new HideWindowListener(window, exit); 681 exit.setHideSharedElementsCallback(listener); 682 exit.startExit(); 683 } 684 return opts; 685 } 686 687 /** 688 * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])} 689 * animation must be stopped and the Views reset. This can happen if there was an error 690 * from startActivity or a springboard activity and the animation should stop and reset. 691 * 692 * @hide 693 */ 694 public static void stopSharedElementAnimation(Window window) { 695 final View decorView = window.getDecorView(); 696 if (decorView == null) { 697 return; 698 } 699 final ExitTransitionCoordinator exit = (ExitTransitionCoordinator) 700 decorView.getTag(com.android.internal.R.id.cross_task_transition); 701 if (exit != null) { 702 exit.cancelPendingTransitions(); 703 decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, null); 704 TransitionManager.endTransitions((ViewGroup) decorView); 705 exit.resetViews(); 706 exit.clearState(); 707 decorView.setVisibility(View.VISIBLE); 708 } 709 } 710 711 static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window, 712 ActivityOptions opts, SharedElementCallback callback, 713 Pair<View, String>[] sharedElements) { 714 if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { 715 opts.mAnimationType = ANIM_DEFAULT; 716 return null; 717 } 718 opts.mAnimationType = ANIM_SCENE_TRANSITION; 719 720 ArrayList<String> names = new ArrayList<String>(); 721 ArrayList<View> views = new ArrayList<View>(); 722 723 if (sharedElements != null) { 724 for (int i = 0; i < sharedElements.length; i++) { 725 Pair<View, String> sharedElement = sharedElements[i]; 726 String sharedElementName = sharedElement.second; 727 if (sharedElementName == null) { 728 throw new IllegalArgumentException("Shared element name must not be null"); 729 } 730 names.add(sharedElementName); 731 View view = sharedElement.first; 732 if (view == null) { 733 throw new IllegalArgumentException("Shared element must not be null"); 734 } 735 views.add(sharedElement.first); 736 } 737 } 738 739 ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window, 740 callback, names, names, views, false); 741 opts.mTransitionReceiver = exit; 742 opts.mSharedElementNames = names; 743 opts.mIsReturning = (activity == null); 744 if (activity == null) { 745 opts.mExitCoordinatorIndex = -1; 746 } else { 747 opts.mExitCoordinatorIndex = 748 activity.mActivityTransitionState.addExitTransitionCoordinator(exit); 749 } 750 return exit; 751 } 752 753 /** @hide */ 754 static ActivityOptions makeSceneTransitionAnimation(Activity activity, 755 ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames, 756 int resultCode, Intent resultData) { 757 ActivityOptions opts = new ActivityOptions(); 758 opts.mAnimationType = ANIM_SCENE_TRANSITION; 759 opts.mSharedElementNames = sharedElementNames; 760 opts.mTransitionReceiver = exitCoordinator; 761 opts.mIsReturning = true; 762 opts.mResultCode = resultCode; 763 opts.mResultData = resultData; 764 opts.mExitCoordinatorIndex = 765 activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator); 766 return opts; 767 } 768 769 /** 770 * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be 771 * presented to the user but will instead be only available through the recents task list. 772 * In addition, the new task wil be affiliated with the launching activity's task. 773 * Affiliated tasks are grouped together in the recents task list. 774 * 775 * <p>This behavior is not supported for activities with {@link 776 * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of 777 * <code>singleInstance</code> or <code>singleTask</code>. 778 */ 779 public static ActivityOptions makeTaskLaunchBehind() { 780 final ActivityOptions opts = new ActivityOptions(); 781 opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND; 782 return opts; 783 } 784 785 /** 786 * Create a basic ActivityOptions that has no special animation associated with it. 787 * Other options can still be set. 788 */ 789 public static ActivityOptions makeBasic() { 790 final ActivityOptions opts = new ActivityOptions(); 791 return opts; 792 } 793 794 /** @hide */ 795 public boolean getLaunchTaskBehind() { 796 return mAnimationType == ANIM_LAUNCH_TASK_BEHIND; 797 } 798 799 private ActivityOptions() { 800 } 801 802 /** @hide */ 803 public ActivityOptions(Bundle opts) { 804 // If the remote side sent us bad parcelables, they won't get the 805 // results they want, which is their loss. 806 opts.setDefusable(true); 807 808 mPackageName = opts.getString(KEY_PACKAGE_NAME); 809 try { 810 mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT); 811 } catch (RuntimeException e) { 812 Slog.w(TAG, e); 813 } 814 mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS); 815 mAnimationType = opts.getInt(KEY_ANIM_TYPE); 816 switch (mAnimationType) { 817 case ANIM_CUSTOM: 818 mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0); 819 mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0); 820 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 821 opts.getBinder(KEY_ANIM_START_LISTENER)); 822 break; 823 824 case ANIM_CUSTOM_IN_PLACE: 825 mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0); 826 break; 827 828 case ANIM_SCALE_UP: 829 case ANIM_CLIP_REVEAL: 830 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 831 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 832 mWidth = opts.getInt(KEY_ANIM_WIDTH, 0); 833 mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0); 834 break; 835 836 case ANIM_THUMBNAIL_SCALE_UP: 837 case ANIM_THUMBNAIL_SCALE_DOWN: 838 case ANIM_THUMBNAIL_ASPECT_SCALE_UP: 839 case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: 840 // Unpackage the GraphicBuffer from the parceled thumbnail 841 final GraphicBuffer buffer = opts.getParcelable(KEY_ANIM_THUMBNAIL); 842 if (buffer != null) { 843 mThumbnail = Bitmap.createHardwareBitmap(buffer); 844 } 845 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 846 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 847 mWidth = opts.getInt(KEY_ANIM_WIDTH, 0); 848 mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0); 849 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 850 opts.getBinder(KEY_ANIM_START_LISTENER)); 851 break; 852 853 case ANIM_SCENE_TRANSITION: 854 mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER); 855 mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false); 856 mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS); 857 mResultData = opts.getParcelable(KEY_RESULT_DATA); 858 mResultCode = opts.getInt(KEY_RESULT_CODE); 859 mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX); 860 break; 861 } 862 mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY); 863 mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID); 864 mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1); 865 mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false); 866 mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false); 867 mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT); 868 mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean( 869 KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, false); 870 if (opts.containsKey(KEY_ANIM_SPECS)) { 871 Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS); 872 mAnimSpecs = new AppTransitionAnimationSpec[specs.length]; 873 for (int i = specs.length - 1; i >= 0; i--) { 874 mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i]; 875 } 876 } 877 if (opts.containsKey(KEY_ANIMATION_FINISHED_LISTENER)) { 878 mAnimationFinishedListener = IRemoteCallback.Stub.asInterface( 879 opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER)); 880 } 881 mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT); 882 mAppVerificationBundle = opts.getBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE); 883 if (opts.containsKey(KEY_SPECS_FUTURE)) { 884 mSpecsFuture = IAppTransitionAnimationSpecsFuture.Stub.asInterface(opts.getBinder( 885 KEY_SPECS_FUTURE)); 886 } 887 } 888 889 /** 890 * Sets the bounds (window size) that the activity should be launched in. 891 * Rect position should be provided in pixels and in screen coordinates. 892 * Set to null explicitly for fullscreen. 893 * <p> 894 * <strong>NOTE:<strong/> This value is ignored on devices that don't have 895 * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or 896 * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled. 897 * @param screenSpacePixelRect Launch bounds to use for the activity or null for fullscreen. 898 */ 899 public ActivityOptions setLaunchBounds(@Nullable Rect screenSpacePixelRect) { 900 mLaunchBounds = screenSpacePixelRect != null ? new Rect(screenSpacePixelRect) : null; 901 return this; 902 } 903 904 /** @hide */ 905 public String getPackageName() { 906 return mPackageName; 907 } 908 909 /** 910 * Returns the bounds that should be used to launch the activity. 911 * @see #setLaunchBounds(Rect) 912 * @return Bounds used to launch the activity. 913 */ 914 @Nullable 915 public Rect getLaunchBounds() { 916 return mLaunchBounds; 917 } 918 919 /** @hide */ 920 public int getAnimationType() { 921 return mAnimationType; 922 } 923 924 /** @hide */ 925 public int getCustomEnterResId() { 926 return mCustomEnterResId; 927 } 928 929 /** @hide */ 930 public int getCustomExitResId() { 931 return mCustomExitResId; 932 } 933 934 /** @hide */ 935 public int getCustomInPlaceResId() { 936 return mCustomInPlaceResId; 937 } 938 939 /** 940 * The thumbnail is copied into a hardware bitmap when it is bundled and sent to the system, so 941 * it should always be backed by a GraphicBuffer on the other end. 942 * 943 * @hide 944 */ 945 public GraphicBuffer getThumbnail() { 946 return mThumbnail != null ? mThumbnail.createGraphicBufferHandle() : null; 947 } 948 949 /** @hide */ 950 public int getStartX() { 951 return mStartX; 952 } 953 954 /** @hide */ 955 public int getStartY() { 956 return mStartY; 957 } 958 959 /** @hide */ 960 public int getWidth() { 961 return mWidth; 962 } 963 964 /** @hide */ 965 public int getHeight() { 966 return mHeight; 967 } 968 969 /** @hide */ 970 public IRemoteCallback getOnAnimationStartListener() { 971 return mAnimationStartedListener; 972 } 973 974 /** @hide */ 975 public IRemoteCallback getAnimationFinishedListener() { 976 return mAnimationFinishedListener; 977 } 978 979 /** @hide */ 980 public int getExitCoordinatorKey() { return mExitCoordinatorIndex; } 981 982 /** @hide */ 983 public void abort() { 984 if (mAnimationStartedListener != null) { 985 try { 986 mAnimationStartedListener.sendResult(null); 987 } catch (RemoteException e) { 988 } 989 } 990 } 991 992 /** @hide */ 993 public boolean isReturning() { 994 return mIsReturning; 995 } 996 997 /** 998 * Returns whether or not the ActivityOptions was created with 999 * {@link #startSharedElementAnimation(Window, Pair[])}. 1000 * 1001 * @hide 1002 */ 1003 boolean isCrossTask() { 1004 return mExitCoordinatorIndex < 0; 1005 } 1006 1007 /** @hide */ 1008 public ArrayList<String> getSharedElementNames() { 1009 return mSharedElementNames; 1010 } 1011 1012 /** @hide */ 1013 public ResultReceiver getResultReceiver() { return mTransitionReceiver; } 1014 1015 /** @hide */ 1016 public int getResultCode() { return mResultCode; } 1017 1018 /** @hide */ 1019 public Intent getResultData() { return mResultData; } 1020 1021 /** @hide */ 1022 public PendingIntent getUsageTimeReport() { 1023 return mUsageTimeReport; 1024 } 1025 1026 /** @hide */ 1027 public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; } 1028 1029 /** @hide */ 1030 public IAppTransitionAnimationSpecsFuture getSpecsFuture() { 1031 return mSpecsFuture; 1032 } 1033 1034 /** @hide */ 1035 public static ActivityOptions fromBundle(Bundle bOptions) { 1036 return bOptions != null ? new ActivityOptions(bOptions) : null; 1037 } 1038 1039 /** @hide */ 1040 public static void abort(ActivityOptions options) { 1041 if (options != null) { 1042 options.abort(); 1043 } 1044 } 1045 1046 /** 1047 * Gets the id of the display where activity should be launched. 1048 * @return The id of the display where activity should be launched, 1049 * {@link android.view.Display#INVALID_DISPLAY} if not set. 1050 * @see #setLaunchDisplayId(int) 1051 */ 1052 public int getLaunchDisplayId() { 1053 return mLaunchDisplayId; 1054 } 1055 1056 /** 1057 * Sets the id of the display where activity should be launched. 1058 * An app can launch activities on public displays or private displays that are owned by the app 1059 * or where an app already has activities. Otherwise, trying to launch on a private display 1060 * or providing an invalid display id will result in an exception. 1061 * <p> 1062 * Setting launch display id will be ignored on devices that don't have 1063 * {@link android.content.pm.PackageManager#FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS}. 1064 * @param launchDisplayId The id of the display where the activity should be launched. 1065 * @return {@code this} {@link ActivityOptions} instance. 1066 */ 1067 public ActivityOptions setLaunchDisplayId(int launchDisplayId) { 1068 mLaunchDisplayId = launchDisplayId; 1069 return this; 1070 } 1071 1072 /** @hide */ 1073 public int getLaunchStackId() { 1074 return mLaunchStackId; 1075 } 1076 1077 /** @hide */ 1078 @TestApi 1079 public void setLaunchStackId(int launchStackId) { 1080 mLaunchStackId = launchStackId; 1081 } 1082 1083 /** 1084 * Sets the task the activity will be launched in. 1085 * @hide 1086 */ 1087 @TestApi 1088 public void setLaunchTaskId(int taskId) { 1089 mLaunchTaskId = taskId; 1090 } 1091 1092 /** 1093 * @hide 1094 */ 1095 public int getLaunchTaskId() { 1096 return mLaunchTaskId; 1097 } 1098 1099 /** 1100 * Set's whether the activity launched with this option should be a task overlay. That is the 1101 * activity will always be the top activity of the task. If {@param canResume} is true, then 1102 * the task will also not be moved to the front of the stack. 1103 * @hide 1104 */ 1105 @TestApi 1106 public void setTaskOverlay(boolean taskOverlay, boolean canResume) { 1107 mTaskOverlay = taskOverlay; 1108 mTaskOverlayCanResume = canResume; 1109 } 1110 1111 /** 1112 * @hide 1113 */ 1114 public boolean getTaskOverlay() { 1115 return mTaskOverlay; 1116 } 1117 1118 /** 1119 * @hide 1120 */ 1121 public boolean canTaskOverlayResume() { 1122 return mTaskOverlayCanResume; 1123 } 1124 1125 /** @hide */ 1126 public int getDockCreateMode() { 1127 return mDockCreateMode; 1128 } 1129 1130 /** @hide */ 1131 public void setDockCreateMode(int dockCreateMode) { 1132 mDockCreateMode = dockCreateMode; 1133 } 1134 1135 /** @hide */ 1136 public void setDisallowEnterPictureInPictureWhileLaunching(boolean disallow) { 1137 mDisallowEnterPictureInPictureWhileLaunching = disallow; 1138 } 1139 1140 /** @hide */ 1141 public boolean disallowEnterPictureInPictureWhileLaunching() { 1142 return mDisallowEnterPictureInPictureWhileLaunching; 1143 } 1144 1145 /** 1146 * Update the current values in this ActivityOptions from those supplied 1147 * in <var>otherOptions</var>. Any values 1148 * defined in <var>otherOptions</var> replace those in the base options. 1149 */ 1150 public void update(ActivityOptions otherOptions) { 1151 if (otherOptions.mPackageName != null) { 1152 mPackageName = otherOptions.mPackageName; 1153 } 1154 mUsageTimeReport = otherOptions.mUsageTimeReport; 1155 mTransitionReceiver = null; 1156 mSharedElementNames = null; 1157 mIsReturning = false; 1158 mResultData = null; 1159 mResultCode = 0; 1160 mExitCoordinatorIndex = 0; 1161 mAnimationType = otherOptions.mAnimationType; 1162 switch (otherOptions.mAnimationType) { 1163 case ANIM_CUSTOM: 1164 mCustomEnterResId = otherOptions.mCustomEnterResId; 1165 mCustomExitResId = otherOptions.mCustomExitResId; 1166 mThumbnail = null; 1167 if (mAnimationStartedListener != null) { 1168 try { 1169 mAnimationStartedListener.sendResult(null); 1170 } catch (RemoteException e) { 1171 } 1172 } 1173 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 1174 break; 1175 case ANIM_CUSTOM_IN_PLACE: 1176 mCustomInPlaceResId = otherOptions.mCustomInPlaceResId; 1177 break; 1178 case ANIM_SCALE_UP: 1179 mStartX = otherOptions.mStartX; 1180 mStartY = otherOptions.mStartY; 1181 mWidth = otherOptions.mWidth; 1182 mHeight = otherOptions.mHeight; 1183 if (mAnimationStartedListener != null) { 1184 try { 1185 mAnimationStartedListener.sendResult(null); 1186 } catch (RemoteException e) { 1187 } 1188 } 1189 mAnimationStartedListener = null; 1190 break; 1191 case ANIM_THUMBNAIL_SCALE_UP: 1192 case ANIM_THUMBNAIL_SCALE_DOWN: 1193 case ANIM_THUMBNAIL_ASPECT_SCALE_UP: 1194 case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: 1195 mThumbnail = otherOptions.mThumbnail; 1196 mStartX = otherOptions.mStartX; 1197 mStartY = otherOptions.mStartY; 1198 mWidth = otherOptions.mWidth; 1199 mHeight = otherOptions.mHeight; 1200 if (mAnimationStartedListener != null) { 1201 try { 1202 mAnimationStartedListener.sendResult(null); 1203 } catch (RemoteException e) { 1204 } 1205 } 1206 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 1207 break; 1208 case ANIM_SCENE_TRANSITION: 1209 mTransitionReceiver = otherOptions.mTransitionReceiver; 1210 mSharedElementNames = otherOptions.mSharedElementNames; 1211 mIsReturning = otherOptions.mIsReturning; 1212 mThumbnail = null; 1213 mAnimationStartedListener = null; 1214 mResultData = otherOptions.mResultData; 1215 mResultCode = otherOptions.mResultCode; 1216 mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex; 1217 break; 1218 } 1219 mAnimSpecs = otherOptions.mAnimSpecs; 1220 mAnimationFinishedListener = otherOptions.mAnimationFinishedListener; 1221 mSpecsFuture = otherOptions.mSpecsFuture; 1222 } 1223 1224 /** 1225 * Returns the created options as a Bundle, which can be passed to 1226 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) 1227 * Context.startActivity(Intent, Bundle)} and related methods. 1228 * Note that the returned Bundle is still owned by the ActivityOptions 1229 * object; you must not modify it, but can supply it to the startActivity 1230 * methods that take an options Bundle. 1231 */ 1232 public Bundle toBundle() { 1233 Bundle b = new Bundle(); 1234 if (mPackageName != null) { 1235 b.putString(KEY_PACKAGE_NAME, mPackageName); 1236 } 1237 if (mLaunchBounds != null) { 1238 b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds); 1239 } 1240 b.putInt(KEY_ANIM_TYPE, mAnimationType); 1241 if (mUsageTimeReport != null) { 1242 b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport); 1243 } 1244 switch (mAnimationType) { 1245 case ANIM_CUSTOM: 1246 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId); 1247 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId); 1248 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 1249 != null ? mAnimationStartedListener.asBinder() : null); 1250 break; 1251 case ANIM_CUSTOM_IN_PLACE: 1252 b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId); 1253 break; 1254 case ANIM_SCALE_UP: 1255 case ANIM_CLIP_REVEAL: 1256 b.putInt(KEY_ANIM_START_X, mStartX); 1257 b.putInt(KEY_ANIM_START_Y, mStartY); 1258 b.putInt(KEY_ANIM_WIDTH, mWidth); 1259 b.putInt(KEY_ANIM_HEIGHT, mHeight); 1260 break; 1261 case ANIM_THUMBNAIL_SCALE_UP: 1262 case ANIM_THUMBNAIL_SCALE_DOWN: 1263 case ANIM_THUMBNAIL_ASPECT_SCALE_UP: 1264 case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: 1265 // Once we parcel the thumbnail for transfering over to the system, create a copy of 1266 // the bitmap to a hardware bitmap and pass through the GraphicBuffer 1267 if (mThumbnail != null) { 1268 final Bitmap hwBitmap = mThumbnail.copy(Config.HARDWARE, false /* isMutable */); 1269 if (hwBitmap != null) { 1270 b.putParcelable(KEY_ANIM_THUMBNAIL, hwBitmap.createGraphicBufferHandle()); 1271 } else { 1272 Slog.w(TAG, "Failed to copy thumbnail"); 1273 } 1274 } 1275 b.putInt(KEY_ANIM_START_X, mStartX); 1276 b.putInt(KEY_ANIM_START_Y, mStartY); 1277 b.putInt(KEY_ANIM_WIDTH, mWidth); 1278 b.putInt(KEY_ANIM_HEIGHT, mHeight); 1279 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 1280 != null ? mAnimationStartedListener.asBinder() : null); 1281 break; 1282 case ANIM_SCENE_TRANSITION: 1283 if (mTransitionReceiver != null) { 1284 b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver); 1285 } 1286 b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning); 1287 b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames); 1288 b.putParcelable(KEY_RESULT_DATA, mResultData); 1289 b.putInt(KEY_RESULT_CODE, mResultCode); 1290 b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex); 1291 break; 1292 } 1293 b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId); 1294 b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId); 1295 b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId); 1296 b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay); 1297 b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume); 1298 b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode); 1299 b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, 1300 mDisallowEnterPictureInPictureWhileLaunching); 1301 if (mAnimSpecs != null) { 1302 b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs); 1303 } 1304 if (mAnimationFinishedListener != null) { 1305 b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder()); 1306 } 1307 if (mSpecsFuture != null) { 1308 b.putBinder(KEY_SPECS_FUTURE, mSpecsFuture.asBinder()); 1309 } 1310 b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint); 1311 if (mAppVerificationBundle != null) { 1312 b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle); 1313 } 1314 1315 return b; 1316 } 1317 1318 /** 1319 * Ask the the system track that time the user spends in the app being launched, and 1320 * report it back once done. The report will be sent to the given receiver, with 1321 * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES} 1322 * filled in. 1323 * 1324 * <p>The time interval tracked is from launching this activity until the user leaves 1325 * that activity's flow. They are considered to stay in the flow as long as 1326 * new activities are being launched or returned to from the original flow, 1327 * even if this crosses package or task boundaries. For example, if the originator 1328 * starts an activity to view an image, and while there the user selects to share, 1329 * which launches their email app in a new task, and they complete the share, the 1330 * time during that entire operation will be included until they finally hit back from 1331 * the original image viewer activity.</p> 1332 * 1333 * <p>The user is considered to complete a flow once they switch to another 1334 * activity that is not part of the tracked flow. This may happen, for example, by 1335 * using the notification shade, launcher, or recents to launch or switch to another 1336 * app. Simply going in to these navigation elements does not break the flow (although 1337 * the launcher and recents stops time tracking of the session); it is the act of 1338 * going somewhere else that completes the tracking.</p> 1339 * 1340 * @param receiver A broadcast receiver that willl receive the report. 1341 */ 1342 public void requestUsageTimeReport(PendingIntent receiver) { 1343 mUsageTimeReport = receiver; 1344 } 1345 1346 /** 1347 * Return the filtered options only meant to be seen by the target activity itself 1348 * @hide 1349 */ 1350 public ActivityOptions forTargetActivity() { 1351 if (mAnimationType == ANIM_SCENE_TRANSITION) { 1352 final ActivityOptions result = new ActivityOptions(); 1353 result.update(this); 1354 return result; 1355 } 1356 1357 return null; 1358 } 1359 1360 /** 1361 * Returns the rotation animation set by {@link setRotationAnimationHint} or -1 1362 * if unspecified. 1363 * @hide 1364 */ 1365 public int getRotationAnimationHint() { 1366 return mRotationAnimationHint; 1367 } 1368 1369 1370 /** 1371 * Set a rotation animation to be used if launching the activity 1372 * triggers an orientation change, or -1 to clear. See 1373 * {@link android.view.WindowManager.LayoutParams} for rotation 1374 * animation values. 1375 * @hide 1376 */ 1377 public void setRotationAnimationHint(int hint) { 1378 mRotationAnimationHint = hint; 1379 } 1380 1381 /** 1382 * Pop the extra verification bundle for the installer. 1383 * This removes the bundle from the ActivityOptions to make sure the installer bundle 1384 * is only available once. 1385 * @hide 1386 */ 1387 public Bundle popAppVerificationBundle() { 1388 Bundle out = mAppVerificationBundle; 1389 mAppVerificationBundle = null; 1390 return out; 1391 } 1392 1393 /** 1394 * Set the {@link Bundle} that is provided to the app installer for additional verification 1395 * if the call to {@link Context#startActivity} results in an app being installed. 1396 * 1397 * This Bundle is not provided to any other app besides the installer. 1398 */ 1399 public ActivityOptions setAppVerificationBundle(Bundle bundle) { 1400 mAppVerificationBundle = bundle; 1401 return this; 1402 1403 } 1404 1405 /** @hide */ 1406 @Override 1407 public String toString() { 1408 return "ActivityOptions(" + hashCode() + "), mPackageName=" + mPackageName 1409 + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY=" 1410 + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight; 1411 } 1412 1413 private static class HideWindowListener extends TransitionListenerAdapter 1414 implements ExitTransitionCoordinator.HideSharedElementsCallback { 1415 private final Window mWindow; 1416 private final ExitTransitionCoordinator mExit; 1417 private final boolean mWaitingForTransition; 1418 private boolean mTransitionEnded; 1419 private boolean mSharedElementHidden; 1420 private ArrayList<View> mSharedElements; 1421 1422 public HideWindowListener(Window window, ExitTransitionCoordinator exit) { 1423 mWindow = window; 1424 mExit = exit; 1425 mSharedElements = new ArrayList<>(exit.mSharedElements); 1426 Transition transition = mWindow.getExitTransition(); 1427 if (transition != null) { 1428 transition.addListener(this); 1429 mWaitingForTransition = true; 1430 } else { 1431 mWaitingForTransition = false; 1432 } 1433 View decorView = mWindow.getDecorView(); 1434 if (decorView != null) { 1435 if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) { 1436 throw new IllegalStateException( 1437 "Cannot start a transition while one is running"); 1438 } 1439 decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit); 1440 } 1441 } 1442 1443 @Override 1444 public void onTransitionEnd(Transition transition) { 1445 mTransitionEnded = true; 1446 hideWhenDone(); 1447 transition.removeListener(this); 1448 } 1449 1450 @Override 1451 public void hideSharedElements() { 1452 mSharedElementHidden = true; 1453 hideWhenDone(); 1454 } 1455 1456 private void hideWhenDone() { 1457 if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) { 1458 mExit.resetViews(); 1459 int numSharedElements = mSharedElements.size(); 1460 for (int i = 0; i < numSharedElements; i++) { 1461 View view = mSharedElements.get(i); 1462 view.requestLayout(); 1463 } 1464 View decorView = mWindow.getDecorView(); 1465 if (decorView != null) { 1466 decorView.setTagInternal( 1467 com.android.internal.R.id.cross_task_transition, null); 1468 decorView.setVisibility(View.GONE); 1469 } 1470 } 1471 } 1472 } 1473 } 1474