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