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