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 android.content.Context; 20 import android.graphics.Bitmap; 21 import android.os.Bundle; 22 import android.os.Handler; 23 import android.os.IRemoteCallback; 24 import android.os.RemoteException; 25 import android.view.View; 26 27 /** 28 * Helper class for building an options Bundle that can be used with 29 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) 30 * Context.startActivity(Intent, Bundle)} and related methods. 31 */ 32 public class ActivityOptions { 33 /** 34 * The package name that created the options. 35 * @hide 36 */ 37 public static final String KEY_PACKAGE_NAME = "android:packageName"; 38 39 /** 40 * Type of animation that arguments specify. 41 * @hide 42 */ 43 public static final String KEY_ANIM_TYPE = "android:animType"; 44 45 /** 46 * Custom enter animation resource ID. 47 * @hide 48 */ 49 public static final String KEY_ANIM_ENTER_RES_ID = "android:animEnterRes"; 50 51 /** 52 * Custom exit animation resource ID. 53 * @hide 54 */ 55 public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes"; 56 57 /** 58 * Bitmap for thumbnail animation. 59 * @hide 60 */ 61 public static final String KEY_ANIM_THUMBNAIL = "android:animThumbnail"; 62 63 /** 64 * Start X position of thumbnail animation. 65 * @hide 66 */ 67 public static final String KEY_ANIM_START_X = "android:animStartX"; 68 69 /** 70 * Start Y position of thumbnail animation. 71 * @hide 72 */ 73 public static final String KEY_ANIM_START_Y = "android:animStartY"; 74 75 /** 76 * Initial width of the animation. 77 * @hide 78 */ 79 public static final String KEY_ANIM_START_WIDTH = "android:animStartWidth"; 80 81 /** 82 * Initial height of the animation. 83 * @hide 84 */ 85 public static final String KEY_ANIM_START_HEIGHT = "android:animStartHeight"; 86 87 /** 88 * Callback for when animation is started. 89 * @hide 90 */ 91 public static final String KEY_ANIM_START_LISTENER = "android:animStartListener"; 92 93 /** @hide */ 94 public static final int ANIM_NONE = 0; 95 /** @hide */ 96 public static final int ANIM_CUSTOM = 1; 97 /** @hide */ 98 public static final int ANIM_SCALE_UP = 2; 99 /** @hide */ 100 public static final int ANIM_THUMBNAIL = 3; 101 /** @hide */ 102 public static final int ANIM_THUMBNAIL_DELAYED = 4; 103 104 private String mPackageName; 105 private int mAnimationType = ANIM_NONE; 106 private int mCustomEnterResId; 107 private int mCustomExitResId; 108 private Bitmap mThumbnail; 109 private int mStartX; 110 private int mStartY; 111 private int mStartWidth; 112 private int mStartHeight; 113 private IRemoteCallback mAnimationStartedListener; 114 115 /** 116 * Create an ActivityOptions specifying a custom animation to run when 117 * the activity is displayed. 118 * 119 * @param context Who is defining this. This is the application that the 120 * animation resources will be loaded from. 121 * @param enterResId A resource ID of the animation resource to use for 122 * the incoming activity. Use 0 for no animation. 123 * @param exitResId A resource ID of the animation resource to use for 124 * the outgoing activity. Use 0 for no animation. 125 * @return Returns a new ActivityOptions object that you can use to 126 * supply these options as the options Bundle when starting an activity. 127 */ 128 public static ActivityOptions makeCustomAnimation(Context context, 129 int enterResId, int exitResId) { 130 return makeCustomAnimation(context, enterResId, exitResId, null, null); 131 } 132 133 /** 134 * Create an ActivityOptions specifying a custom animation to run when 135 * the activity is displayed. 136 * 137 * @param context Who is defining this. This is the application that the 138 * animation resources will be loaded from. 139 * @param enterResId A resource ID of the animation resource to use for 140 * the incoming activity. Use 0 for no animation. 141 * @param exitResId A resource ID of the animation resource to use for 142 * the outgoing activity. Use 0 for no animation. 143 * @param handler If <var>listener</var> is non-null this must be a valid 144 * Handler on which to dispatch the callback; otherwise it should be null. 145 * @param listener Optional OnAnimationStartedListener to find out when the 146 * requested animation has started running. If for some reason the animation 147 * is not executed, the callback will happen immediately. 148 * @return Returns a new ActivityOptions object that you can use to 149 * supply these options as the options Bundle when starting an activity. 150 * @hide 151 */ 152 public static ActivityOptions makeCustomAnimation(Context context, 153 int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) { 154 ActivityOptions opts = new ActivityOptions(); 155 opts.mPackageName = context.getPackageName(); 156 opts.mAnimationType = ANIM_CUSTOM; 157 opts.mCustomEnterResId = enterResId; 158 opts.mCustomExitResId = exitResId; 159 opts.setListener(handler, listener); 160 return opts; 161 } 162 163 private void setListener(Handler handler, OnAnimationStartedListener listener) { 164 if (listener != null) { 165 final Handler h = handler; 166 final OnAnimationStartedListener finalListener = listener; 167 mAnimationStartedListener = new IRemoteCallback.Stub() { 168 @Override public void sendResult(Bundle data) throws RemoteException { 169 h.post(new Runnable() { 170 @Override public void run() { 171 finalListener.onAnimationStarted(); 172 } 173 }); 174 } 175 }; 176 } 177 } 178 179 /** 180 * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation} 181 * to find out when the given animation has started running. 182 * @hide 183 */ 184 public interface OnAnimationStartedListener { 185 void onAnimationStarted(); 186 } 187 188 /** 189 * Create an ActivityOptions specifying an animation where the new 190 * activity is scaled from a small originating area of the screen to 191 * its final full representation. 192 * 193 * <p>If the Intent this is being used with has not set its 194 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, 195 * those bounds will be filled in for you based on the initial 196 * bounds passed in here. 197 * 198 * @param source The View that the new activity is animating from. This 199 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 200 * @param startX The x starting location of the new activity, relative to <var>source</var>. 201 * @param startY The y starting location of the activity, relative to <var>source</var>. 202 * @param startWidth The initial width of the new activity. 203 * @param startHeight The initial height of the new activity. 204 * @return Returns a new ActivityOptions object that you can use to 205 * supply these options as the options Bundle when starting an activity. 206 */ 207 public static ActivityOptions makeScaleUpAnimation(View source, 208 int startX, int startY, int startWidth, int startHeight) { 209 ActivityOptions opts = new ActivityOptions(); 210 opts.mPackageName = source.getContext().getPackageName(); 211 opts.mAnimationType = ANIM_SCALE_UP; 212 int[] pts = new int[2]; 213 source.getLocationOnScreen(pts); 214 opts.mStartX = pts[0] + startX; 215 opts.mStartY = pts[1] + startY; 216 opts.mStartWidth = startWidth; 217 opts.mStartHeight = startHeight; 218 return opts; 219 } 220 221 /** 222 * Create an ActivityOptions specifying an animation where a thumbnail 223 * is scaled from a given position to the new activity window that is 224 * being started. 225 * 226 * <p>If the Intent this is being used with has not set its 227 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, 228 * those bounds will be filled in for you based on the initial 229 * thumbnail location and size provided here. 230 * 231 * @param source The View that this thumbnail is animating from. This 232 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 233 * @param thumbnail The bitmap that will be shown as the initial thumbnail 234 * of the animation. 235 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 236 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 237 * @return Returns a new ActivityOptions object that you can use to 238 * supply these options as the options Bundle when starting an activity. 239 */ 240 public static ActivityOptions makeThumbnailScaleUpAnimation(View source, 241 Bitmap thumbnail, int startX, int startY) { 242 return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null); 243 } 244 245 /** 246 * Create an ActivityOptions specifying an animation where a thumbnail 247 * is scaled from a given position to the new activity window that is 248 * being started. 249 * 250 * @param source The View that this thumbnail is animating from. This 251 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 252 * @param thumbnail The bitmap that will be shown as the initial thumbnail 253 * of the animation. 254 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 255 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 256 * @param listener Optional OnAnimationStartedListener to find out when the 257 * requested animation has started running. If for some reason the animation 258 * is not executed, the callback will happen immediately. 259 * @return Returns a new ActivityOptions object that you can use to 260 * supply these options as the options Bundle when starting an activity. 261 * @hide 262 */ 263 public static ActivityOptions makeThumbnailScaleUpAnimation(View source, 264 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { 265 return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, false); 266 } 267 268 /** 269 * Create an ActivityOptions specifying an animation where a thumbnail 270 * is scaled from a given position to the new activity window that is 271 * being started. Before the animation, there is a short delay. 272 * 273 * @param source The View that this thumbnail is animating from. This 274 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 275 * @param thumbnail The bitmap that will be shown as the initial thumbnail 276 * of the animation. 277 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 278 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 279 * @param listener Optional OnAnimationStartedListener to find out when the 280 * requested animation has started running. If for some reason the animation 281 * is not executed, the callback will happen immediately. 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 * @hide 285 */ 286 public static ActivityOptions makeDelayedThumbnailScaleUpAnimation(View source, 287 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { 288 return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, true); 289 } 290 291 private static ActivityOptions makeThumbnailScaleUpAnimation(View source, 292 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener, 293 boolean delayed) { 294 ActivityOptions opts = new ActivityOptions(); 295 opts.mPackageName = source.getContext().getPackageName(); 296 opts.mAnimationType = delayed ? ANIM_THUMBNAIL_DELAYED : ANIM_THUMBNAIL; 297 opts.mThumbnail = thumbnail; 298 int[] pts = new int[2]; 299 source.getLocationOnScreen(pts); 300 opts.mStartX = pts[0] + startX; 301 opts.mStartY = pts[1] + startY; 302 opts.setListener(source.getHandler(), listener); 303 return opts; 304 } 305 306 private ActivityOptions() { 307 } 308 309 /** @hide */ 310 public ActivityOptions(Bundle opts) { 311 mPackageName = opts.getString(KEY_PACKAGE_NAME); 312 mAnimationType = opts.getInt(KEY_ANIM_TYPE); 313 if (mAnimationType == ANIM_CUSTOM) { 314 mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0); 315 mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0); 316 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 317 opts.getIBinder(KEY_ANIM_START_LISTENER)); 318 } else if (mAnimationType == ANIM_SCALE_UP) { 319 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 320 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 321 mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0); 322 mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0); 323 } else if (mAnimationType == ANIM_THUMBNAIL || 324 mAnimationType == ANIM_THUMBNAIL_DELAYED) { 325 mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL); 326 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 327 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 328 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 329 opts.getIBinder(KEY_ANIM_START_LISTENER)); 330 } 331 } 332 333 /** @hide */ 334 public String getPackageName() { 335 return mPackageName; 336 } 337 338 /** @hide */ 339 public int getAnimationType() { 340 return mAnimationType; 341 } 342 343 /** @hide */ 344 public int getCustomEnterResId() { 345 return mCustomEnterResId; 346 } 347 348 /** @hide */ 349 public int getCustomExitResId() { 350 return mCustomExitResId; 351 } 352 353 /** @hide */ 354 public Bitmap getThumbnail() { 355 return mThumbnail; 356 } 357 358 /** @hide */ 359 public int getStartX() { 360 return mStartX; 361 } 362 363 /** @hide */ 364 public int getStartY() { 365 return mStartY; 366 } 367 368 /** @hide */ 369 public int getStartWidth() { 370 return mStartWidth; 371 } 372 373 /** @hide */ 374 public int getStartHeight() { 375 return mStartHeight; 376 } 377 378 /** @hide */ 379 public IRemoteCallback getOnAnimationStartListener() { 380 return mAnimationStartedListener; 381 } 382 383 /** @hide */ 384 public void abort() { 385 if (mAnimationStartedListener != null) { 386 try { 387 mAnimationStartedListener.sendResult(null); 388 } catch (RemoteException e) { 389 } 390 } 391 } 392 393 /** @hide */ 394 public static void abort(Bundle options) { 395 if (options != null) { 396 (new ActivityOptions(options)).abort(); 397 } 398 } 399 400 /** 401 * Update the current values in this ActivityOptions from those supplied 402 * in <var>otherOptions</var>. Any values 403 * defined in <var>otherOptions</var> replace those in the base options. 404 */ 405 public void update(ActivityOptions otherOptions) { 406 if (otherOptions.mPackageName != null) { 407 mPackageName = otherOptions.mPackageName; 408 } 409 switch (otherOptions.mAnimationType) { 410 case ANIM_CUSTOM: 411 mAnimationType = otherOptions.mAnimationType; 412 mCustomEnterResId = otherOptions.mCustomEnterResId; 413 mCustomExitResId = otherOptions.mCustomExitResId; 414 mThumbnail = null; 415 if (otherOptions.mAnimationStartedListener != null) { 416 try { 417 otherOptions.mAnimationStartedListener.sendResult(null); 418 } catch (RemoteException e) { 419 } 420 } 421 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 422 break; 423 case ANIM_SCALE_UP: 424 mAnimationType = otherOptions.mAnimationType; 425 mStartX = otherOptions.mStartX; 426 mStartY = otherOptions.mStartY; 427 mStartWidth = otherOptions.mStartWidth; 428 mStartHeight = otherOptions.mStartHeight; 429 if (otherOptions.mAnimationStartedListener != null) { 430 try { 431 otherOptions.mAnimationStartedListener.sendResult(null); 432 } catch (RemoteException e) { 433 } 434 } 435 mAnimationStartedListener = null; 436 break; 437 case ANIM_THUMBNAIL: 438 case ANIM_THUMBNAIL_DELAYED: 439 mAnimationType = otherOptions.mAnimationType; 440 mThumbnail = otherOptions.mThumbnail; 441 mStartX = otherOptions.mStartX; 442 mStartY = otherOptions.mStartY; 443 if (otherOptions.mAnimationStartedListener != null) { 444 try { 445 otherOptions.mAnimationStartedListener.sendResult(null); 446 } catch (RemoteException e) { 447 } 448 } 449 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 450 break; 451 } 452 } 453 454 /** 455 * Returns the created options as a Bundle, which can be passed to 456 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) 457 * Context.startActivity(Intent, Bundle)} and related methods. 458 * Note that the returned Bundle is still owned by the ActivityOptions 459 * object; you must not modify it, but can supply it to the startActivity 460 * methods that take an options Bundle. 461 */ 462 public Bundle toBundle() { 463 Bundle b = new Bundle(); 464 if (mPackageName != null) { 465 b.putString(KEY_PACKAGE_NAME, mPackageName); 466 } 467 switch (mAnimationType) { 468 case ANIM_CUSTOM: 469 b.putInt(KEY_ANIM_TYPE, mAnimationType); 470 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId); 471 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId); 472 b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 473 != null ? mAnimationStartedListener.asBinder() : null); 474 break; 475 case ANIM_SCALE_UP: 476 b.putInt(KEY_ANIM_TYPE, mAnimationType); 477 b.putInt(KEY_ANIM_START_X, mStartX); 478 b.putInt(KEY_ANIM_START_Y, mStartY); 479 b.putInt(KEY_ANIM_START_WIDTH, mStartWidth); 480 b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight); 481 break; 482 case ANIM_THUMBNAIL: 483 case ANIM_THUMBNAIL_DELAYED: 484 b.putInt(KEY_ANIM_TYPE, mAnimationType); 485 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail); 486 b.putInt(KEY_ANIM_START_X, mStartX); 487 b.putInt(KEY_ANIM_START_Y, mStartY); 488 b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 489 != null ? mAnimationStartedListener.asBinder() : null); 490 break; 491 } 492 return b; 493 } 494 } 495