ActivityOptions.java revision d6107a3170df61d9e776fcd5666acfc9135c6f16
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.app; 18 19import android.content.Context; 20import android.graphics.Bitmap; 21import android.os.Bundle; 22import android.os.Handler; 23import android.os.IBinder; 24import android.os.IRemoteCallback; 25import android.os.RemoteException; 26import android.os.ResultReceiver; 27import android.transition.Transition; 28import android.util.Log; 29import android.util.Pair; 30import android.view.View; 31 32import java.util.ArrayList; 33import java.util.Map; 34 35/** 36 * Helper class for building an options Bundle that can be used with 37 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) 38 * Context.startActivity(Intent, Bundle)} and related methods. 39 */ 40public class ActivityOptions { 41 private static final String TAG = "ActivityOptions"; 42 43 /** 44 * The package name that created the options. 45 * @hide 46 */ 47 public static final String KEY_PACKAGE_NAME = "android:packageName"; 48 49 /** 50 * Type of animation that arguments specify. 51 * @hide 52 */ 53 public static final String KEY_ANIM_TYPE = "android:animType"; 54 55 /** 56 * Custom enter animation resource ID. 57 * @hide 58 */ 59 public static final String KEY_ANIM_ENTER_RES_ID = "android:animEnterRes"; 60 61 /** 62 * Custom exit animation resource ID. 63 * @hide 64 */ 65 public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes"; 66 67 /** 68 * Bitmap for thumbnail animation. 69 * @hide 70 */ 71 public static final String KEY_ANIM_THUMBNAIL = "android:animThumbnail"; 72 73 /** 74 * Start X position of thumbnail animation. 75 * @hide 76 */ 77 public static final String KEY_ANIM_START_X = "android:animStartX"; 78 79 /** 80 * Start Y position of thumbnail animation. 81 * @hide 82 */ 83 public static final String KEY_ANIM_START_Y = "android:animStartY"; 84 85 /** 86 * Initial width of the animation. 87 * @hide 88 */ 89 public static final String KEY_ANIM_START_WIDTH = "android:animStartWidth"; 90 91 /** 92 * Initial height of the animation. 93 * @hide 94 */ 95 public static final String KEY_ANIM_START_HEIGHT = "android:animStartHeight"; 96 97 /** 98 * Callback for when animation is started. 99 * @hide 100 */ 101 public static final String KEY_ANIM_START_LISTENER = "android:animStartListener"; 102 103 /** 104 * For Activity transitions, the calling Activity's TransitionListener used to 105 * notify the called Activity when the shared element and the exit transitions 106 * complete. 107 */ 108 private static final String KEY_TRANSITION_COMPLETE_LISTENER 109 = "android:transitionCompleteListener"; 110 111 /** 112 * For Activity transitions, the called Activity's listener to receive calls 113 * when transitions complete. 114 */ 115 private static final String KEY_TRANSITION_TARGET_LISTENER = "android:transitionTargetListener"; 116 117 /** 118 * The names of shared elements that are transitioned to the started Activity. 119 * This is also the name of shared elements that the started Activity accepted. 120 */ 121 private static final String KEY_SHARED_ELEMENT_NAMES = "android:shared_element_names"; 122 123 /** 124 * The shared elements names of the views in the calling Activity. 125 */ 126 private static final String KEY_LOCAL_ELEMENT_NAMES = "android:local_element_names"; 127 128 /** @hide */ 129 public static final int ANIM_NONE = 0; 130 /** @hide */ 131 public static final int ANIM_CUSTOM = 1; 132 /** @hide */ 133 public static final int ANIM_SCALE_UP = 2; 134 /** @hide */ 135 public static final int ANIM_THUMBNAIL_SCALE_UP = 3; 136 /** @hide */ 137 public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4; 138 /** @hide */ 139 public static final int ANIM_SCENE_TRANSITION = 5; 140 141 private static final int MSG_SET_LISTENER = 100; 142 private static final int MSG_HIDE_SHARED_ELEMENTS = 101; 143 private static final int MSG_PREPARE_RESTORE = 102; 144 private static final int MSG_RESTORE = 103; 145 146 private String mPackageName; 147 private int mAnimationType = ANIM_NONE; 148 private int mCustomEnterResId; 149 private int mCustomExitResId; 150 private Bitmap mThumbnail; 151 private int mStartX; 152 private int mStartY; 153 private int mStartWidth; 154 private int mStartHeight; 155 private IRemoteCallback mAnimationStartedListener; 156 private ResultReceiver mTransitionCompleteListener; 157 private ArrayList<String> mSharedElementNames; 158 private ArrayList<String> mLocalElementNames; 159 160 /** 161 * Create an ActivityOptions specifying a custom animation to run when 162 * the activity is displayed. 163 * 164 * @param context Who is defining this. This is the application that the 165 * animation resources will be loaded from. 166 * @param enterResId A resource ID of the animation resource to use for 167 * the incoming activity. Use 0 for no animation. 168 * @param exitResId A resource ID of the animation resource to use for 169 * the outgoing activity. Use 0 for no animation. 170 * @return Returns a new ActivityOptions object that you can use to 171 * supply these options as the options Bundle when starting an activity. 172 */ 173 public static ActivityOptions makeCustomAnimation(Context context, 174 int enterResId, int exitResId) { 175 return makeCustomAnimation(context, enterResId, exitResId, null, null); 176 } 177 178 /** 179 * Create an ActivityOptions specifying a custom animation to run when 180 * the activity is displayed. 181 * 182 * @param context Who is defining this. This is the application that the 183 * animation resources will be loaded from. 184 * @param enterResId A resource ID of the animation resource to use for 185 * the incoming activity. Use 0 for no animation. 186 * @param exitResId A resource ID of the animation resource to use for 187 * the outgoing activity. Use 0 for no animation. 188 * @param handler If <var>listener</var> is non-null this must be a valid 189 * Handler on which to dispatch the callback; otherwise it should be null. 190 * @param listener Optional OnAnimationStartedListener to find out when the 191 * requested animation has started running. If for some reason the animation 192 * is not executed, the callback will happen immediately. 193 * @return Returns a new ActivityOptions object that you can use to 194 * supply these options as the options Bundle when starting an activity. 195 * @hide 196 */ 197 public static ActivityOptions makeCustomAnimation(Context context, 198 int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) { 199 ActivityOptions opts = new ActivityOptions(); 200 opts.mPackageName = context.getPackageName(); 201 opts.mAnimationType = ANIM_CUSTOM; 202 opts.mCustomEnterResId = enterResId; 203 opts.mCustomExitResId = exitResId; 204 opts.setOnAnimationStartedListener(handler, listener); 205 return opts; 206 } 207 208 private void setOnAnimationStartedListener(Handler handler, 209 OnAnimationStartedListener listener) { 210 if (listener != null) { 211 final Handler h = handler; 212 final OnAnimationStartedListener finalListener = listener; 213 mAnimationStartedListener = new IRemoteCallback.Stub() { 214 @Override public void sendResult(Bundle data) throws RemoteException { 215 h.post(new Runnable() { 216 @Override public void run() { 217 finalListener.onAnimationStarted(); 218 } 219 }); 220 } 221 }; 222 } 223 } 224 225 /** 226 * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation} 227 * to find out when the given animation has started running. 228 * @hide 229 */ 230 public interface OnAnimationStartedListener { 231 void onAnimationStarted(); 232 } 233 234 /** @hide */ 235 public interface ActivityTransitionTarget { 236 void sharedElementTransitionComplete(Bundle transitionArgs); 237 void exitTransitionComplete(); 238 } 239 240 /** 241 * Create an ActivityOptions specifying an animation where the new 242 * activity is scaled from a small originating area of the screen to 243 * its final full representation. 244 * 245 * <p>If the Intent this is being used with has not set its 246 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, 247 * those bounds will be filled in for you based on the initial 248 * bounds passed in here. 249 * 250 * @param source The View that the new activity is animating from. This 251 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 252 * @param startX The x starting location of the new activity, relative to <var>source</var>. 253 * @param startY The y starting location of the activity, relative to <var>source</var>. 254 * @param startWidth The initial width of the new activity. 255 * @param startHeight The initial height of the new activity. 256 * @return Returns a new ActivityOptions object that you can use to 257 * supply these options as the options Bundle when starting an activity. 258 */ 259 public static ActivityOptions makeScaleUpAnimation(View source, 260 int startX, int startY, int startWidth, int startHeight) { 261 ActivityOptions opts = new ActivityOptions(); 262 opts.mPackageName = source.getContext().getPackageName(); 263 opts.mAnimationType = ANIM_SCALE_UP; 264 int[] pts = new int[2]; 265 source.getLocationOnScreen(pts); 266 opts.mStartX = pts[0] + startX; 267 opts.mStartY = pts[1] + startY; 268 opts.mStartWidth = startWidth; 269 opts.mStartHeight = startHeight; 270 return opts; 271 } 272 273 /** 274 * Create an ActivityOptions specifying an animation where a thumbnail 275 * is scaled from a given position to the new activity window that is 276 * being started. 277 * 278 * <p>If the Intent this is being used with has not set its 279 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, 280 * those bounds will be filled in for you based on the initial 281 * thumbnail location and size provided here. 282 * 283 * @param source The View that this thumbnail is animating from. This 284 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 285 * @param thumbnail The bitmap that will be shown as the initial thumbnail 286 * of the animation. 287 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 288 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 289 * @return Returns a new ActivityOptions object that you can use to 290 * supply these options as the options Bundle when starting an activity. 291 */ 292 public static ActivityOptions makeThumbnailScaleUpAnimation(View source, 293 Bitmap thumbnail, int startX, int startY) { 294 return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null); 295 } 296 297 /** 298 * Create an ActivityOptions specifying an animation where a thumbnail 299 * is scaled from a given position to the new activity window that is 300 * being started. 301 * 302 * @param source The View that this thumbnail is animating from. This 303 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 304 * @param thumbnail The bitmap that will be shown as the initial thumbnail 305 * of the animation. 306 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 307 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 308 * @param listener Optional OnAnimationStartedListener to find out when the 309 * requested animation has started running. If for some reason the animation 310 * is not executed, the callback will happen immediately. 311 * @return Returns a new ActivityOptions object that you can use to 312 * supply these options as the options Bundle when starting an activity. 313 * @hide 314 */ 315 public static ActivityOptions makeThumbnailScaleUpAnimation(View source, 316 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { 317 return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true); 318 } 319 320 /** 321 * Create an ActivityOptions specifying an animation where an activity window 322 * is scaled from a given position to a thumbnail at a specified location. 323 * 324 * @param source The View that this thumbnail is animating to. This 325 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 326 * @param thumbnail The bitmap that will be shown as the final thumbnail 327 * of the animation. 328 * @param startX The x end location of the bitmap, relative to <var>source</var>. 329 * @param startY The y end location of the bitmap, relative to <var>source</var>. 330 * @param listener Optional OnAnimationStartedListener to find out when the 331 * requested animation has started running. If for some reason the animation 332 * is not executed, the callback will happen immediately. 333 * @return Returns a new ActivityOptions object that you can use to 334 * supply these options as the options Bundle when starting an activity. 335 * @hide 336 */ 337 public static ActivityOptions makeThumbnailScaleDownAnimation(View source, 338 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { 339 return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, false); 340 } 341 342 private static ActivityOptions makeThumbnailAnimation(View source, 343 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener, 344 boolean scaleUp) { 345 ActivityOptions opts = new ActivityOptions(); 346 opts.mPackageName = source.getContext().getPackageName(); 347 opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN; 348 opts.mThumbnail = thumbnail; 349 int[] pts = new int[2]; 350 source.getLocationOnScreen(pts); 351 opts.mStartX = pts[0] + startX; 352 opts.mStartY = pts[1] + startY; 353 opts.setOnAnimationStartedListener(source.getHandler(), listener); 354 return opts; 355 } 356 357 /** 358 * Create an ActivityOptions to transition between Activities using cross-Activity scene 359 * animations. This method carries the position of one shared element to the started Activity. 360 * 361 * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be 362 * enabled on the calling Activity to cause an exit transition. The same must be in 363 * the called Activity to get an entering transition.</p> 364 * @param sharedElement The View to transition to the started Activity. sharedElement must 365 * have a non-null sharedElementName. 366 * @param sharedElementName The shared element name as used in the target Activity. This may 367 * be null if it has the same name as sharedElement. 368 * @return Returns a new ActivityOptions object that you can use to 369 * supply these options as the options Bundle when starting an activity. 370 */ 371 public static ActivityOptions makeSceneTransitionAnimation(View sharedElement, 372 String sharedElementName) { 373 return makeSceneTransitionAnimation( 374 new Pair<View, String>(sharedElement, sharedElementName)); 375 } 376 377 /** 378 * Create an ActivityOptions to transition between Activities using cross-Activity scene 379 * animations. This method carries the position of multiple shared elements to the started 380 * Activity. 381 * 382 * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be 383 * enabled on the calling Activity to cause an exit transition. The same must be in 384 * the called Activity to get an entering transition.</p> 385 * @param sharedElements The View to transition to the started Activity along with the 386 * shared element name as used in the started Activity. The view 387 * must have a non-null sharedElementName. 388 * @return Returns a new ActivityOptions object that you can use to 389 * supply these options as the options Bundle when starting an activity. 390 */ 391 public static ActivityOptions makeSceneTransitionAnimation( 392 Pair<View, String>... sharedElements) { 393 ActivityOptions opts = new ActivityOptions(); 394 opts.mAnimationType = ANIM_SCENE_TRANSITION; 395 opts.mSharedElementNames = new ArrayList<String>(); 396 opts.mLocalElementNames = new ArrayList<String>(); 397 398 if (sharedElements != null) { 399 for (Pair<View, String> sharedElement : sharedElements) { 400 opts.addSharedElement(sharedElement.first, sharedElement.second); 401 } 402 } 403 return opts; 404 } 405 406 private ActivityOptions() { 407 } 408 409 /** @hide */ 410 public ActivityOptions(Bundle opts) { 411 mPackageName = opts.getString(KEY_PACKAGE_NAME); 412 mAnimationType = opts.getInt(KEY_ANIM_TYPE); 413 switch (mAnimationType) { 414 case ANIM_CUSTOM: 415 mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0); 416 mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0); 417 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 418 opts.getBinder(KEY_ANIM_START_LISTENER)); 419 break; 420 421 case ANIM_SCALE_UP: 422 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 423 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 424 mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0); 425 mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0); 426 break; 427 428 case ANIM_THUMBNAIL_SCALE_UP: 429 case ANIM_THUMBNAIL_SCALE_DOWN: 430 mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL); 431 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 432 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 433 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 434 opts.getBinder(KEY_ANIM_START_LISTENER)); 435 break; 436 437 case ANIM_SCENE_TRANSITION: 438 mTransitionCompleteListener = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER); 439 mSharedElementNames = opts.getStringArrayList(KEY_SHARED_ELEMENT_NAMES); 440 mLocalElementNames = opts.getStringArrayList(KEY_LOCAL_ELEMENT_NAMES); 441 break; 442 } 443 } 444 445 /** @hide */ 446 public String getPackageName() { 447 return mPackageName; 448 } 449 450 /** @hide */ 451 public int getAnimationType() { 452 return mAnimationType; 453 } 454 455 /** @hide */ 456 public int getCustomEnterResId() { 457 return mCustomEnterResId; 458 } 459 460 /** @hide */ 461 public int getCustomExitResId() { 462 return mCustomExitResId; 463 } 464 465 /** @hide */ 466 public Bitmap getThumbnail() { 467 return mThumbnail; 468 } 469 470 /** @hide */ 471 public int getStartX() { 472 return mStartX; 473 } 474 475 /** @hide */ 476 public int getStartY() { 477 return mStartY; 478 } 479 480 /** @hide */ 481 public int getStartWidth() { 482 return mStartWidth; 483 } 484 485 /** @hide */ 486 public int getStartHeight() { 487 return mStartHeight; 488 } 489 490 /** @hide */ 491 public IRemoteCallback getOnAnimationStartListener() { 492 return mAnimationStartedListener; 493 } 494 495 /** @hide */ 496 public ArrayList<String> getSharedElementNames() { return mSharedElementNames; } 497 498 /** @hide */ 499 public ArrayList<String> getLocalElementNames() { return mLocalElementNames; } 500 501 /** @hide */ 502 public void dispatchSceneTransitionStarted(final ActivityTransitionTarget target, 503 ArrayList<String> sharedElementNames) { 504 if (mTransitionCompleteListener != null) { 505 IRemoteCallback callback = new IRemoteCallback.Stub() { 506 @Override 507 public void sendResult(Bundle data) throws RemoteException { 508 if (data == null) { 509 target.exitTransitionComplete(); 510 } else { 511 target.sharedElementTransitionComplete(data); 512 } 513 } 514 }; 515 Bundle bundle = new Bundle(); 516 bundle.putBinder(KEY_TRANSITION_TARGET_LISTENER, callback.asBinder()); 517 bundle.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, sharedElementNames); 518 mTransitionCompleteListener.send(MSG_SET_LISTENER, bundle); 519 } 520 } 521 522 /** @hide */ 523 public void dispatchSharedElementsReady() { 524 if (mTransitionCompleteListener != null) { 525 mTransitionCompleteListener.send(MSG_HIDE_SHARED_ELEMENTS, null); 526 } 527 } 528 529 /** @hide */ 530 public void dispatchPrepareRestore() { 531 if (mTransitionCompleteListener != null) { 532 mTransitionCompleteListener.send(MSG_PREPARE_RESTORE, null); 533 } 534 } 535 536 /** @hide */ 537 public void dispatchRestore(Bundle sharedElementsArgs) { 538 if (mTransitionCompleteListener != null) { 539 mTransitionCompleteListener.send(MSG_RESTORE, sharedElementsArgs); 540 } 541 } 542 543 /** @hide */ 544 public void abort() { 545 if (mAnimationStartedListener != null) { 546 try { 547 mAnimationStartedListener.sendResult(null); 548 } catch (RemoteException e) { 549 } 550 } 551 } 552 553 /** @hide */ 554 public static void abort(Bundle options) { 555 if (options != null) { 556 (new ActivityOptions(options)).abort(); 557 } 558 } 559 560 /** 561 * Update the current values in this ActivityOptions from those supplied 562 * in <var>otherOptions</var>. Any values 563 * defined in <var>otherOptions</var> replace those in the base options. 564 */ 565 public void update(ActivityOptions otherOptions) { 566 if (otherOptions.mPackageName != null) { 567 mPackageName = otherOptions.mPackageName; 568 } 569 mSharedElementNames = null; 570 mLocalElementNames = null; 571 switch (otherOptions.mAnimationType) { 572 case ANIM_CUSTOM: 573 mAnimationType = otherOptions.mAnimationType; 574 mCustomEnterResId = otherOptions.mCustomEnterResId; 575 mCustomExitResId = otherOptions.mCustomExitResId; 576 mThumbnail = null; 577 if (mAnimationStartedListener != null) { 578 try { 579 mAnimationStartedListener.sendResult(null); 580 } catch (RemoteException e) { 581 } 582 } 583 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 584 mTransitionCompleteListener = null; 585 break; 586 case ANIM_SCALE_UP: 587 mAnimationType = otherOptions.mAnimationType; 588 mStartX = otherOptions.mStartX; 589 mStartY = otherOptions.mStartY; 590 mStartWidth = otherOptions.mStartWidth; 591 mStartHeight = otherOptions.mStartHeight; 592 if (mAnimationStartedListener != null) { 593 try { 594 mAnimationStartedListener.sendResult(null); 595 } catch (RemoteException e) { 596 } 597 } 598 mAnimationStartedListener = null; 599 mTransitionCompleteListener = null; 600 break; 601 case ANIM_THUMBNAIL_SCALE_UP: 602 case ANIM_THUMBNAIL_SCALE_DOWN: 603 mAnimationType = otherOptions.mAnimationType; 604 mThumbnail = otherOptions.mThumbnail; 605 mStartX = otherOptions.mStartX; 606 mStartY = otherOptions.mStartY; 607 if (mAnimationStartedListener != null) { 608 try { 609 mAnimationStartedListener.sendResult(null); 610 } catch (RemoteException e) { 611 } 612 } 613 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 614 mTransitionCompleteListener = null; 615 break; 616 case ANIM_SCENE_TRANSITION: 617 mAnimationType = otherOptions.mAnimationType; 618 mTransitionCompleteListener = otherOptions.mTransitionCompleteListener; 619 mThumbnail = null; 620 mAnimationStartedListener = null; 621 mSharedElementNames = otherOptions.mSharedElementNames; 622 mLocalElementNames = otherOptions.mLocalElementNames; 623 break; 624 } 625 } 626 627 /** 628 * Returns the created options as a Bundle, which can be passed to 629 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) 630 * Context.startActivity(Intent, Bundle)} and related methods. 631 * Note that the returned Bundle is still owned by the ActivityOptions 632 * object; you must not modify it, but can supply it to the startActivity 633 * methods that take an options Bundle. 634 */ 635 public Bundle toBundle() { 636 Bundle b = new Bundle(); 637 if (mPackageName != null) { 638 b.putString(KEY_PACKAGE_NAME, mPackageName); 639 } 640 switch (mAnimationType) { 641 case ANIM_CUSTOM: 642 b.putInt(KEY_ANIM_TYPE, mAnimationType); 643 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId); 644 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId); 645 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 646 != null ? mAnimationStartedListener.asBinder() : null); 647 break; 648 case ANIM_SCALE_UP: 649 b.putInt(KEY_ANIM_TYPE, mAnimationType); 650 b.putInt(KEY_ANIM_START_X, mStartX); 651 b.putInt(KEY_ANIM_START_Y, mStartY); 652 b.putInt(KEY_ANIM_START_WIDTH, mStartWidth); 653 b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight); 654 break; 655 case ANIM_THUMBNAIL_SCALE_UP: 656 case ANIM_THUMBNAIL_SCALE_DOWN: 657 b.putInt(KEY_ANIM_TYPE, mAnimationType); 658 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail); 659 b.putInt(KEY_ANIM_START_X, mStartX); 660 b.putInt(KEY_ANIM_START_Y, mStartY); 661 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 662 != null ? mAnimationStartedListener.asBinder() : null); 663 break; 664 case ANIM_SCENE_TRANSITION: 665 b.putInt(KEY_ANIM_TYPE, mAnimationType); 666 if (mTransitionCompleteListener != null) { 667 b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionCompleteListener); 668 } 669 b.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, mSharedElementNames); 670 b.putStringArrayList(KEY_LOCAL_ELEMENT_NAMES, mLocalElementNames); 671 break; 672 } 673 return b; 674 } 675 676 /** 677 * Return the filtered options only meant to be seen by the target activity itself 678 * @hide 679 */ 680 public ActivityOptions forTargetActivity() { 681 if (mAnimationType == ANIM_SCENE_TRANSITION) { 682 final ActivityOptions result = new ActivityOptions(); 683 result.update(this); 684 return result; 685 } 686 687 return null; 688 } 689 690 /** @hide */ 691 public void addSharedElements(Map<String, View> sharedElements) { 692 for (Map.Entry<String, View> entry : sharedElements.entrySet()) { 693 addSharedElement(entry.getValue(), entry.getKey()); 694 } 695 } 696 697 /** @hide */ 698 public void updateSceneTransitionAnimation(Transition exitTransition, 699 Transition sharedElementTransition, SharedElementSource sharedElementSource) { 700 mTransitionCompleteListener = new ExitTransitionListener(exitTransition, 701 sharedElementTransition, sharedElementSource); 702 } 703 704 private void addSharedElement(View view, String name) { 705 String sharedElementName = view.getSharedElementName(); 706 if (name == null) { 707 name = sharedElementName; 708 } 709 mSharedElementNames.add(name); 710 mLocalElementNames.add(sharedElementName); 711 } 712 713 /** @hide */ 714 public interface SharedElementSource { 715 Bundle getSharedElementExitState(); 716 void acceptedSharedElements(ArrayList<String> sharedElementNames); 717 void hideSharedElements(); 718 void restore(Bundle sharedElementState); 719 void prepareForRestore(); 720 } 721 722 private static class ExitTransitionListener extends ResultReceiver 723 implements Transition.TransitionListener { 724 private boolean mSharedElementNotified; 725 private IRemoteCallback mTransitionCompleteCallback; 726 private boolean mExitComplete; 727 private boolean mSharedElementComplete; 728 private SharedElementSource mSharedElementSource; 729 730 public ExitTransitionListener(Transition exitTransition, Transition sharedElementTransition, 731 SharedElementSource sharedElementSource) { 732 super(null); 733 mSharedElementSource = sharedElementSource; 734 exitTransition.addListener(this); 735 sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() { 736 @Override 737 public void onTransitionEnd(Transition transition) { 738 mSharedElementComplete = true; 739 notifySharedElement(); 740 transition.removeListener(this); 741 } 742 }); 743 } 744 745 @Override 746 protected void onReceiveResult(int resultCode, Bundle resultData) { 747 switch (resultCode) { 748 case MSG_SET_LISTENER: 749 IBinder listener = resultData.getBinder(KEY_TRANSITION_TARGET_LISTENER); 750 mTransitionCompleteCallback = IRemoteCallback.Stub.asInterface(listener); 751 ArrayList<String> sharedElementNames 752 = resultData.getStringArrayList(KEY_SHARED_ELEMENT_NAMES); 753 mSharedElementSource.acceptedSharedElements(sharedElementNames); 754 notifySharedElement(); 755 notifyExit(); 756 break; 757 case MSG_HIDE_SHARED_ELEMENTS: 758 mSharedElementSource.hideSharedElements(); 759 break; 760 case MSG_PREPARE_RESTORE: 761 mSharedElementSource.prepareForRestore(); 762 break; 763 case MSG_RESTORE: 764 mSharedElementSource.restore(resultData); 765 break; 766 } 767 } 768 769 @Override 770 public void onTransitionStart(Transition transition) { 771 } 772 773 @Override 774 public void onTransitionEnd(Transition transition) { 775 mExitComplete = true; 776 notifyExit(); 777 transition.removeListener(this); 778 } 779 780 @Override 781 public void onTransitionCancel(Transition transition) { 782 onTransitionEnd(transition); 783 } 784 785 @Override 786 public void onTransitionPause(Transition transition) { 787 } 788 789 @Override 790 public void onTransitionResume(Transition transition) { 791 } 792 793 private void notifySharedElement() { 794 if (!mSharedElementNotified && mSharedElementComplete 795 && mTransitionCompleteCallback != null) { 796 mSharedElementNotified = true; 797 try { 798 Bundle sharedElementState = mSharedElementSource.getSharedElementExitState(); 799 mTransitionCompleteCallback.sendResult(sharedElementState); 800 } catch (RemoteException e) { 801 Log.w(TAG, "Couldn't notify that the transition ended", e); 802 } 803 } 804 } 805 806 private void notifyExit() { 807 if (mExitComplete && mTransitionCompleteCallback != null) { 808 try { 809 mTransitionCompleteCallback.sendResult(null); 810 } catch (RemoteException e) { 811 Log.w(TAG, "Couldn't notify that the transition ended", e); 812 } 813 } 814 } 815 } 816} 817