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_SCALE_UP = 3; 101 /** @hide */ 102 public static final int ANIM_THUMBNAIL_SCALE_DOWN = 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 makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true); 266 } 267 268 /** 269 * Create an ActivityOptions specifying an animation where an activity window 270 * is scaled from a given position to a thumbnail at a specified location. 271 * 272 * @param source The View that this thumbnail is animating to. This 273 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 274 * @param thumbnail The bitmap that will be shown as the final thumbnail 275 * of the animation. 276 * @param startX The x end location of the bitmap, relative to <var>source</var>. 277 * @param startY The y end location of the bitmap, relative to <var>source</var>. 278 * @param listener Optional OnAnimationStartedListener to find out when the 279 * requested animation has started running. If for some reason the animation 280 * is not executed, the callback will happen immediately. 281 * @return Returns a new ActivityOptions object that you can use to 282 * supply these options as the options Bundle when starting an activity. 283 * @hide 284 */ 285 public static ActivityOptions makeThumbnailScaleDownAnimation(View source, 286 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { 287 return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, false); 288 } 289 290 private static ActivityOptions makeThumbnailAnimation(View source, 291 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener, 292 boolean scaleUp) { 293 ActivityOptions opts = new ActivityOptions(); 294 opts.mPackageName = source.getContext().getPackageName(); 295 opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN; 296 opts.mThumbnail = thumbnail; 297 int[] pts = new int[2]; 298 source.getLocationOnScreen(pts); 299 opts.mStartX = pts[0] + startX; 300 opts.mStartY = pts[1] + startY; 301 opts.setListener(source.getHandler(), listener); 302 return opts; 303 } 304 305 private ActivityOptions() { 306 } 307 308 /** @hide */ 309 public ActivityOptions(Bundle opts) { 310 mPackageName = opts.getString(KEY_PACKAGE_NAME); 311 mAnimationType = opts.getInt(KEY_ANIM_TYPE); 312 if (mAnimationType == ANIM_CUSTOM) { 313 mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0); 314 mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0); 315 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 316 opts.getIBinder(KEY_ANIM_START_LISTENER)); 317 } else if (mAnimationType == ANIM_SCALE_UP) { 318 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 319 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 320 mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0); 321 mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0); 322 } else if (mAnimationType == ANIM_THUMBNAIL_SCALE_UP || 323 mAnimationType == ANIM_THUMBNAIL_SCALE_DOWN) { 324 mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL); 325 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 326 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 327 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 328 opts.getIBinder(KEY_ANIM_START_LISTENER)); 329 } 330 } 331 332 /** @hide */ 333 public String getPackageName() { 334 return mPackageName; 335 } 336 337 /** @hide */ 338 public int getAnimationType() { 339 return mAnimationType; 340 } 341 342 /** @hide */ 343 public int getCustomEnterResId() { 344 return mCustomEnterResId; 345 } 346 347 /** @hide */ 348 public int getCustomExitResId() { 349 return mCustomExitResId; 350 } 351 352 /** @hide */ 353 public Bitmap getThumbnail() { 354 return mThumbnail; 355 } 356 357 /** @hide */ 358 public int getStartX() { 359 return mStartX; 360 } 361 362 /** @hide */ 363 public int getStartY() { 364 return mStartY; 365 } 366 367 /** @hide */ 368 public int getStartWidth() { 369 return mStartWidth; 370 } 371 372 /** @hide */ 373 public int getStartHeight() { 374 return mStartHeight; 375 } 376 377 /** @hide */ 378 public IRemoteCallback getOnAnimationStartListener() { 379 return mAnimationStartedListener; 380 } 381 382 /** @hide */ 383 public void abort() { 384 if (mAnimationStartedListener != null) { 385 try { 386 mAnimationStartedListener.sendResult(null); 387 } catch (RemoteException e) { 388 } 389 } 390 } 391 392 /** @hide */ 393 public static void abort(Bundle options) { 394 if (options != null) { 395 (new ActivityOptions(options)).abort(); 396 } 397 } 398 399 /** 400 * Update the current values in this ActivityOptions from those supplied 401 * in <var>otherOptions</var>. Any values 402 * defined in <var>otherOptions</var> replace those in the base options. 403 */ 404 public void update(ActivityOptions otherOptions) { 405 if (otherOptions.mPackageName != null) { 406 mPackageName = otherOptions.mPackageName; 407 } 408 switch (otherOptions.mAnimationType) { 409 case ANIM_CUSTOM: 410 mAnimationType = otherOptions.mAnimationType; 411 mCustomEnterResId = otherOptions.mCustomEnterResId; 412 mCustomExitResId = otherOptions.mCustomExitResId; 413 mThumbnail = null; 414 if (otherOptions.mAnimationStartedListener != null) { 415 try { 416 otherOptions.mAnimationStartedListener.sendResult(null); 417 } catch (RemoteException e) { 418 } 419 } 420 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 421 break; 422 case ANIM_SCALE_UP: 423 mAnimationType = otherOptions.mAnimationType; 424 mStartX = otherOptions.mStartX; 425 mStartY = otherOptions.mStartY; 426 mStartWidth = otherOptions.mStartWidth; 427 mStartHeight = otherOptions.mStartHeight; 428 if (otherOptions.mAnimationStartedListener != null) { 429 try { 430 otherOptions.mAnimationStartedListener.sendResult(null); 431 } catch (RemoteException e) { 432 } 433 } 434 mAnimationStartedListener = null; 435 break; 436 case ANIM_THUMBNAIL_SCALE_UP: 437 case ANIM_THUMBNAIL_SCALE_DOWN: 438 mAnimationType = otherOptions.mAnimationType; 439 mThumbnail = otherOptions.mThumbnail; 440 mStartX = otherOptions.mStartX; 441 mStartY = otherOptions.mStartY; 442 if (otherOptions.mAnimationStartedListener != null) { 443 try { 444 otherOptions.mAnimationStartedListener.sendResult(null); 445 } catch (RemoteException e) { 446 } 447 } 448 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 449 break; 450 } 451 } 452 453 /** 454 * Returns the created options as a Bundle, which can be passed to 455 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) 456 * Context.startActivity(Intent, Bundle)} and related methods. 457 * Note that the returned Bundle is still owned by the ActivityOptions 458 * object; you must not modify it, but can supply it to the startActivity 459 * methods that take an options Bundle. 460 */ 461 public Bundle toBundle() { 462 Bundle b = new Bundle(); 463 if (mPackageName != null) { 464 b.putString(KEY_PACKAGE_NAME, mPackageName); 465 } 466 switch (mAnimationType) { 467 case ANIM_CUSTOM: 468 b.putInt(KEY_ANIM_TYPE, mAnimationType); 469 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId); 470 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId); 471 b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 472 != null ? mAnimationStartedListener.asBinder() : null); 473 break; 474 case ANIM_SCALE_UP: 475 b.putInt(KEY_ANIM_TYPE, mAnimationType); 476 b.putInt(KEY_ANIM_START_X, mStartX); 477 b.putInt(KEY_ANIM_START_Y, mStartY); 478 b.putInt(KEY_ANIM_START_WIDTH, mStartWidth); 479 b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight); 480 break; 481 case ANIM_THUMBNAIL_SCALE_UP: 482 case ANIM_THUMBNAIL_SCALE_DOWN: 483 b.putInt(KEY_ANIM_TYPE, mAnimationType); 484 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail); 485 b.putInt(KEY_ANIM_START_X, mStartX); 486 b.putInt(KEY_ANIM_START_Y, mStartY); 487 b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 488 != null ? mAnimationStartedListener.asBinder() : null); 489 break; 490 } 491 return b; 492 } 493 } 494