1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app; 18 19 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 20 import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 21 22 import android.annotation.Nullable; 23 import android.annotation.TestApi; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.graphics.Bitmap; 27 import android.graphics.Rect; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.IRemoteCallback; 31 import android.os.Parcelable; 32 import android.os.RemoteException; 33 import android.os.ResultReceiver; 34 import android.util.Pair; 35 import android.util.Slog; 36 import android.view.AppTransitionAnimationSpec; 37 import android.view.View; 38 import android.view.Window; 39 40 import java.util.ArrayList; 41 42 /** 43 * Helper class for building an options Bundle that can be used with 44 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) 45 * Context.startActivity(Intent, Bundle)} and related methods. 46 */ 47 public class ActivityOptions { 48 private static final String TAG = "ActivityOptions"; 49 50 /** 51 * A long in the extras delivered by {@link #requestUsageTimeReport} that contains 52 * the total time (in ms) the user spent in the app flow. 53 */ 54 public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time"; 55 56 /** 57 * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains 58 * detailed information about the time spent in each package associated with the app; 59 * each key is a package name, whose value is a long containing the time (in ms). 60 */ 61 public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages"; 62 63 /** 64 * The package name that created the options. 65 * @hide 66 */ 67 public static final String KEY_PACKAGE_NAME = "android:activity.packageName"; 68 69 /** 70 * The bounds (window size) that the activity should be launched in. Set to null explicitly for 71 * full screen. If the key is not found, previous bounds will be preserved. 72 * NOTE: This value is ignored on devices that don't have 73 * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or 74 * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled. 75 * @hide 76 */ 77 public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds"; 78 79 /** 80 * Type of animation that arguments specify. 81 * @hide 82 */ 83 public static final String KEY_ANIM_TYPE = "android:activity.animType"; 84 85 /** 86 * Custom enter animation resource ID. 87 * @hide 88 */ 89 public static final String KEY_ANIM_ENTER_RES_ID = "android:activity.animEnterRes"; 90 91 /** 92 * Custom exit animation resource ID. 93 * @hide 94 */ 95 public static final String KEY_ANIM_EXIT_RES_ID = "android:activity.animExitRes"; 96 97 /** 98 * Custom in-place animation resource ID. 99 * @hide 100 */ 101 public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes"; 102 103 /** 104 * Bitmap for thumbnail animation. 105 * @hide 106 */ 107 public static final String KEY_ANIM_THUMBNAIL = "android:activity.animThumbnail"; 108 109 /** 110 * Start X position of thumbnail animation. 111 * @hide 112 */ 113 public static final String KEY_ANIM_START_X = "android:activity.animStartX"; 114 115 /** 116 * Start Y position of thumbnail animation. 117 * @hide 118 */ 119 public static final String KEY_ANIM_START_Y = "android:activity.animStartY"; 120 121 /** 122 * Initial width of the animation. 123 * @hide 124 */ 125 public static final String KEY_ANIM_WIDTH = "android:activity.animWidth"; 126 127 /** 128 * Initial height of the animation. 129 * @hide 130 */ 131 public static final String KEY_ANIM_HEIGHT = "android:activity.animHeight"; 132 133 /** 134 * Callback for when animation is started. 135 * @hide 136 */ 137 public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener"; 138 139 /** 140 * Callback for when the last frame of the animation is played. 141 * @hide 142 */ 143 private static final String KEY_ANIMATION_FINISHED_LISTENER = 144 "android:activity.animationFinishedListener"; 145 146 /** 147 * Descriptions of app transition animations to be played during the activity launch. 148 */ 149 private static final String KEY_ANIM_SPECS = "android:activity.animSpecs"; 150 151 /** 152 * The stack id the activity should be launched into. 153 * @hide 154 */ 155 private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId"; 156 157 /** 158 * The task id the activity should be launched into. 159 * @hide 160 */ 161 private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId"; 162 163 /** 164 * See {@link #setTaskOverlay}. 165 * @hide 166 */ 167 private static final String KEY_TASK_OVERLAY = "android.activity.taskOverlay"; 168 169 /** 170 * Where the docked stack should be positioned. 171 * @hide 172 */ 173 private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode"; 174 175 /** 176 * For Activity transitions, the calling Activity's TransitionListener used to 177 * notify the called Activity when the shared element and the exit transitions 178 * complete. 179 */ 180 private static final String KEY_TRANSITION_COMPLETE_LISTENER 181 = "android:activity.transitionCompleteListener"; 182 183 private static final String KEY_TRANSITION_IS_RETURNING 184 = "android:activity.transitionIsReturning"; 185 private static final String KEY_TRANSITION_SHARED_ELEMENTS 186 = "android:activity.sharedElementNames"; 187 private static final String KEY_RESULT_DATA = "android:activity.resultData"; 188 private static final String KEY_RESULT_CODE = "android:activity.resultCode"; 189 private static final String KEY_EXIT_COORDINATOR_INDEX 190 = "android:activity.exitCoordinatorIndex"; 191 192 private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport"; 193 194 /** @hide */ 195 public static final int ANIM_NONE = 0; 196 /** @hide */ 197 public static final int ANIM_CUSTOM = 1; 198 /** @hide */ 199 public static final int ANIM_SCALE_UP = 2; 200 /** @hide */ 201 public static final int ANIM_THUMBNAIL_SCALE_UP = 3; 202 /** @hide */ 203 public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4; 204 /** @hide */ 205 public static final int ANIM_SCENE_TRANSITION = 5; 206 /** @hide */ 207 public static final int ANIM_DEFAULT = 6; 208 /** @hide */ 209 public static final int ANIM_LAUNCH_TASK_BEHIND = 7; 210 /** @hide */ 211 public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8; 212 /** @hide */ 213 public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9; 214 /** @hide */ 215 public static final int ANIM_CUSTOM_IN_PLACE = 10; 216 /** @hide */ 217 public static final int ANIM_CLIP_REVEAL = 11; 218 219 private String mPackageName; 220 private Rect mLaunchBounds; 221 private int mAnimationType = ANIM_NONE; 222 private int mCustomEnterResId; 223 private int mCustomExitResId; 224 private int mCustomInPlaceResId; 225 private Bitmap mThumbnail; 226 private int mStartX; 227 private int mStartY; 228 private int mWidth; 229 private int mHeight; 230 private IRemoteCallback mAnimationStartedListener; 231 private IRemoteCallback mAnimationFinishedListener; 232 private ResultReceiver mTransitionReceiver; 233 private boolean mIsReturning; 234 private ArrayList<String> mSharedElementNames; 235 private Intent mResultData; 236 private int mResultCode; 237 private int mExitCoordinatorIndex; 238 private PendingIntent mUsageTimeReport; 239 private int mLaunchStackId = INVALID_STACK_ID; 240 private int mLaunchTaskId = -1; 241 private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 242 private boolean mTaskOverlay; 243 private AppTransitionAnimationSpec mAnimSpecs[]; 244 245 /** 246 * Create an ActivityOptions specifying a custom animation to run when 247 * the activity is displayed. 248 * 249 * @param context Who is defining this. This is the application that the 250 * animation resources will be loaded from. 251 * @param enterResId A resource ID of the animation resource to use for 252 * the incoming activity. Use 0 for no animation. 253 * @param exitResId A resource ID of the animation resource to use for 254 * the outgoing activity. Use 0 for no animation. 255 * @return Returns a new ActivityOptions object that you can use to 256 * supply these options as the options Bundle when starting an activity. 257 */ 258 public static ActivityOptions makeCustomAnimation(Context context, 259 int enterResId, int exitResId) { 260 return makeCustomAnimation(context, enterResId, exitResId, null, null); 261 } 262 263 /** 264 * Create an ActivityOptions specifying a custom animation to run when 265 * the activity is displayed. 266 * 267 * @param context Who is defining this. This is the application that the 268 * animation resources will be loaded from. 269 * @param enterResId A resource ID of the animation resource to use for 270 * the incoming activity. Use 0 for no animation. 271 * @param exitResId A resource ID of the animation resource to use for 272 * the outgoing activity. Use 0 for no animation. 273 * @param handler If <var>listener</var> is non-null this must be a valid 274 * Handler on which to dispatch the callback; otherwise it should be null. 275 * @param listener Optional OnAnimationStartedListener to find out when the 276 * requested animation has started running. If for some reason the animation 277 * is not executed, the callback will happen immediately. 278 * @return Returns a new ActivityOptions object that you can use to 279 * supply these options as the options Bundle when starting an activity. 280 * @hide 281 */ 282 public static ActivityOptions makeCustomAnimation(Context context, 283 int enterResId, int exitResId, Handler handler, OnAnimationStartedListener listener) { 284 ActivityOptions opts = new ActivityOptions(); 285 opts.mPackageName = context.getPackageName(); 286 opts.mAnimationType = ANIM_CUSTOM; 287 opts.mCustomEnterResId = enterResId; 288 opts.mCustomExitResId = exitResId; 289 opts.setOnAnimationStartedListener(handler, listener); 290 return opts; 291 } 292 293 /** 294 * Creates an ActivityOptions specifying a custom animation to run in place on an existing 295 * activity. 296 * 297 * @param context Who is defining this. This is the application that the 298 * animation resources will be loaded from. 299 * @param animId A resource ID of the animation resource to use for 300 * the incoming activity. 301 * @return Returns a new ActivityOptions object that you can use to 302 * supply these options as the options Bundle when running an in-place animation. 303 * @hide 304 */ 305 public static ActivityOptions makeCustomInPlaceAnimation(Context context, int animId) { 306 if (animId == 0) { 307 throw new RuntimeException("You must specify a valid animation."); 308 } 309 310 ActivityOptions opts = new ActivityOptions(); 311 opts.mPackageName = context.getPackageName(); 312 opts.mAnimationType = ANIM_CUSTOM_IN_PLACE; 313 opts.mCustomInPlaceResId = animId; 314 return opts; 315 } 316 317 private void setOnAnimationStartedListener(final Handler handler, 318 final OnAnimationStartedListener listener) { 319 if (listener != null) { 320 mAnimationStartedListener = new IRemoteCallback.Stub() { 321 @Override 322 public void sendResult(Bundle data) throws RemoteException { 323 handler.post(new Runnable() { 324 @Override public void run() { 325 listener.onAnimationStarted(); 326 } 327 }); 328 } 329 }; 330 } 331 } 332 333 /** 334 * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation} 335 * to find out when the given animation has started running. 336 * @hide 337 */ 338 public interface OnAnimationStartedListener { 339 void onAnimationStarted(); 340 } 341 342 private void setOnAnimationFinishedListener(final Handler handler, 343 final OnAnimationFinishedListener listener) { 344 if (listener != null) { 345 mAnimationFinishedListener = new IRemoteCallback.Stub() { 346 @Override 347 public void sendResult(Bundle data) throws RemoteException { 348 handler.post(new Runnable() { 349 @Override 350 public void run() { 351 listener.onAnimationFinished(); 352 } 353 }); 354 } 355 }; 356 } 357 } 358 359 /** 360 * Callback for use with {@link ActivityOptions#makeThumbnailAspectScaleDownAnimation} 361 * to find out when the given animation has drawn its last frame. 362 * @hide 363 */ 364 public interface OnAnimationFinishedListener { 365 void onAnimationFinished(); 366 } 367 368 /** 369 * Create an ActivityOptions specifying an animation where the new 370 * activity is scaled from a small originating area of the screen to 371 * its final full representation. 372 * 373 * <p>If the Intent this is being used with has not set its 374 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, 375 * those bounds will be filled in for you based on the initial 376 * bounds passed in here. 377 * 378 * @param source The View that the new activity is animating from. This 379 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 380 * @param startX The x starting location of the new activity, relative to <var>source</var>. 381 * @param startY The y starting location of the activity, relative to <var>source</var>. 382 * @param width The initial width of the new activity. 383 * @param height The initial height of the new activity. 384 * @return Returns a new ActivityOptions object that you can use to 385 * supply these options as the options Bundle when starting an activity. 386 */ 387 public static ActivityOptions makeScaleUpAnimation(View source, 388 int startX, int startY, int width, int height) { 389 ActivityOptions opts = new ActivityOptions(); 390 opts.mPackageName = source.getContext().getPackageName(); 391 opts.mAnimationType = ANIM_SCALE_UP; 392 int[] pts = new int[2]; 393 source.getLocationOnScreen(pts); 394 opts.mStartX = pts[0] + startX; 395 opts.mStartY = pts[1] + startY; 396 opts.mWidth = width; 397 opts.mHeight = height; 398 return opts; 399 } 400 401 /** 402 * Create an ActivityOptions specifying an animation where the new 403 * activity is revealed from a small originating area of the screen to 404 * its final full representation. 405 * 406 * @param source The View that the new activity is animating from. This 407 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 408 * @param startX The x starting location of the new activity, relative to <var>source</var>. 409 * @param startY The y starting location of the activity, relative to <var>source</var>. 410 * @param width The initial width of the new activity. 411 * @param height The initial height of the new activity. 412 * @return Returns a new ActivityOptions object that you can use to 413 * supply these options as the options Bundle when starting an activity. 414 */ 415 public static ActivityOptions makeClipRevealAnimation(View source, 416 int startX, int startY, int width, int height) { 417 ActivityOptions opts = new ActivityOptions(); 418 opts.mAnimationType = ANIM_CLIP_REVEAL; 419 int[] pts = new int[2]; 420 source.getLocationOnScreen(pts); 421 opts.mStartX = pts[0] + startX; 422 opts.mStartY = pts[1] + startY; 423 opts.mWidth = width; 424 opts.mHeight = height; 425 return opts; 426 } 427 428 /** 429 * Create an ActivityOptions specifying an animation where a thumbnail 430 * is scaled from a given position to the new activity window that is 431 * being started. 432 * 433 * <p>If the Intent this is being used with has not set its 434 * {@link android.content.Intent#setSourceBounds Intent.setSourceBounds}, 435 * those bounds will be filled in for you based on the initial 436 * thumbnail location and size provided here. 437 * 438 * @param source The View that this thumbnail is animating from. This 439 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 440 * @param thumbnail The bitmap that will be shown as the initial thumbnail 441 * of the animation. 442 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 443 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 444 * @return Returns a new ActivityOptions object that you can use to 445 * supply these options as the options Bundle when starting an activity. 446 */ 447 public static ActivityOptions makeThumbnailScaleUpAnimation(View source, 448 Bitmap thumbnail, int startX, int startY) { 449 return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, null); 450 } 451 452 /** 453 * Create an ActivityOptions specifying an animation where a thumbnail 454 * is scaled from a given position to the new activity window that is 455 * being started. 456 * 457 * @param source The View that this thumbnail is animating from. This 458 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 459 * @param thumbnail The bitmap that will be shown as the initial thumbnail 460 * of the animation. 461 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 462 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 463 * @param listener Optional OnAnimationStartedListener to find out when the 464 * requested animation has started running. If for some reason the animation 465 * is not executed, the callback will happen immediately. 466 * @return Returns a new ActivityOptions object that you can use to 467 * supply these options as the options Bundle when starting an activity. 468 * @hide 469 */ 470 public static ActivityOptions makeThumbnailScaleUpAnimation(View source, 471 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { 472 return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, true); 473 } 474 475 /** 476 * Create an ActivityOptions specifying an animation where an activity window 477 * is scaled from a given position to a thumbnail at a specified location. 478 * 479 * @param source The View that this thumbnail is animating to. This 480 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 481 * @param thumbnail The bitmap that will be shown as the final thumbnail 482 * of the animation. 483 * @param startX The x end location of the bitmap, relative to <var>source</var>. 484 * @param startY The y end location of the bitmap, relative to <var>source</var>. 485 * @param listener Optional OnAnimationStartedListener to find out when the 486 * requested animation has started running. If for some reason the animation 487 * is not executed, the callback will happen immediately. 488 * @return Returns a new ActivityOptions object that you can use to 489 * supply these options as the options Bundle when starting an activity. 490 * @hide 491 */ 492 public static ActivityOptions makeThumbnailScaleDownAnimation(View source, 493 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { 494 return makeThumbnailAnimation(source, thumbnail, startX, startY, listener, false); 495 } 496 497 private static ActivityOptions makeThumbnailAnimation(View source, 498 Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener, 499 boolean scaleUp) { 500 ActivityOptions opts = new ActivityOptions(); 501 opts.mPackageName = source.getContext().getPackageName(); 502 opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN; 503 opts.mThumbnail = thumbnail; 504 int[] pts = new int[2]; 505 source.getLocationOnScreen(pts); 506 opts.mStartX = pts[0] + startX; 507 opts.mStartY = pts[1] + startY; 508 opts.setOnAnimationStartedListener(source.getHandler(), listener); 509 return opts; 510 } 511 512 /** 513 * Create an ActivityOptions specifying an animation where the new activity 514 * window and a thumbnail is aspect-scaled to a new location. 515 * 516 * @param source The View that this thumbnail is animating from. This 517 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 518 * @param thumbnail The bitmap that will be shown as the initial thumbnail 519 * of the animation. 520 * @param startX The x starting location of the bitmap, relative to <var>source</var>. 521 * @param startY The y starting location of the bitmap, relative to <var>source</var>. 522 * @param handler If <var>listener</var> is non-null this must be a valid 523 * Handler on which to dispatch the callback; otherwise it should be null. 524 * @param listener Optional OnAnimationStartedListener to find out when the 525 * requested animation has started running. If for some reason the animation 526 * is not executed, the callback will happen immediately. 527 * @return Returns a new ActivityOptions object that you can use to 528 * supply these options as the options Bundle when starting an activity. 529 * @hide 530 */ 531 public static ActivityOptions makeThumbnailAspectScaleUpAnimation(View source, 532 Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight, 533 Handler handler, OnAnimationStartedListener listener) { 534 return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, 535 targetWidth, targetHeight, handler, listener, true); 536 } 537 538 /** 539 * Create an ActivityOptions specifying an animation where the new activity 540 * window and a thumbnail is aspect-scaled to a new location. 541 * 542 * @param source The View that this thumbnail is animating to. This 543 * defines the coordinate space for <var>startX</var> and <var>startY</var>. 544 * @param thumbnail The bitmap that will be shown as the final thumbnail 545 * of the animation. 546 * @param startX The x end location of the bitmap, relative to <var>source</var>. 547 * @param startY The y end location of the bitmap, relative to <var>source</var>. 548 * @param handler If <var>listener</var> is non-null this must be a valid 549 * Handler on which to dispatch the callback; otherwise it should be null. 550 * @param listener Optional OnAnimationStartedListener to find out when the 551 * requested animation has started running. If for some reason the animation 552 * is not executed, the callback will happen immediately. 553 * @return Returns a new ActivityOptions object that you can use to 554 * supply these options as the options Bundle when starting an activity. 555 * @hide 556 */ 557 public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source, 558 Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight, 559 Handler handler, OnAnimationStartedListener listener) { 560 return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, 561 targetWidth, targetHeight, handler, listener, false); 562 } 563 564 private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail, 565 int startX, int startY, int targetWidth, int targetHeight, 566 Handler handler, OnAnimationStartedListener listener, boolean scaleUp) { 567 ActivityOptions opts = new ActivityOptions(); 568 opts.mPackageName = source.getContext().getPackageName(); 569 opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP : 570 ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; 571 opts.mThumbnail = thumbnail; 572 int[] pts = new int[2]; 573 source.getLocationOnScreen(pts); 574 opts.mStartX = pts[0] + startX; 575 opts.mStartY = pts[1] + startY; 576 opts.mWidth = targetWidth; 577 opts.mHeight = targetHeight; 578 opts.setOnAnimationStartedListener(handler, listener); 579 return opts; 580 } 581 582 /** @hide */ 583 public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source, 584 AppTransitionAnimationSpec[] specs, Handler handler, 585 OnAnimationStartedListener onAnimationStartedListener, 586 OnAnimationFinishedListener onAnimationFinishedListener) { 587 ActivityOptions opts = new ActivityOptions(); 588 opts.mPackageName = source.getContext().getPackageName(); 589 opts.mAnimationType = ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; 590 opts.mAnimSpecs = specs; 591 opts.setOnAnimationStartedListener(handler, onAnimationStartedListener); 592 opts.setOnAnimationFinishedListener(handler, onAnimationFinishedListener); 593 return opts; 594 } 595 596 /** 597 * Create an ActivityOptions to transition between Activities using cross-Activity scene 598 * animations. This method carries the position of one shared element to the started Activity. 599 * The position of <code>sharedElement</code> will be used as the epicenter for the 600 * exit Transition. The position of the shared element in the launched Activity will be the 601 * epicenter of its entering Transition. 602 * 603 * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be 604 * enabled on the calling Activity to cause an exit transition. The same must be in 605 * the called Activity to get an entering transition.</p> 606 * @param activity The Activity whose window contains the shared elements. 607 * @param sharedElement The View to transition to the started Activity. 608 * @param sharedElementName The shared element name as used in the target Activity. This 609 * must not be null. 610 * @return Returns a new ActivityOptions object that you can use to 611 * supply these options as the options Bundle when starting an activity. 612 * @see android.transition.Transition#setEpicenterCallback( 613 * android.transition.Transition.EpicenterCallback) 614 */ 615 public static ActivityOptions makeSceneTransitionAnimation(Activity activity, 616 View sharedElement, String sharedElementName) { 617 return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName)); 618 } 619 620 /** 621 * Create an ActivityOptions to transition between Activities using cross-Activity scene 622 * animations. This method carries the position of multiple shared elements to the started 623 * Activity. The position of the first element in sharedElements 624 * will be used as the epicenter for the exit Transition. The position of the associated 625 * shared element in the launched Activity will be the epicenter of its entering Transition. 626 * 627 * <p>This requires {@link android.view.Window#FEATURE_ACTIVITY_TRANSITIONS} to be 628 * enabled on the calling Activity to cause an exit transition. The same must be in 629 * the called Activity to get an entering transition.</p> 630 * @param activity The Activity whose window contains the shared elements. 631 * @param sharedElements The names of the shared elements to transfer to the called 632 * Activity and their associated Views. The Views must each have 633 * a unique shared element name. 634 * @return Returns a new ActivityOptions object that you can use to 635 * supply these options as the options Bundle when starting an activity. 636 * @see android.transition.Transition#setEpicenterCallback( 637 * android.transition.Transition.EpicenterCallback) 638 */ 639 @SafeVarargs 640 public static ActivityOptions makeSceneTransitionAnimation(Activity activity, 641 Pair<View, String>... sharedElements) { 642 ActivityOptions opts = new ActivityOptions(); 643 if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { 644 opts.mAnimationType = ANIM_DEFAULT; 645 return opts; 646 } 647 opts.mAnimationType = ANIM_SCENE_TRANSITION; 648 649 ArrayList<String> names = new ArrayList<String>(); 650 ArrayList<View> views = new ArrayList<View>(); 651 652 if (sharedElements != null) { 653 for (int i = 0; i < sharedElements.length; i++) { 654 Pair<View, String> sharedElement = sharedElements[i]; 655 String sharedElementName = sharedElement.second; 656 if (sharedElementName == null) { 657 throw new IllegalArgumentException("Shared element name must not be null"); 658 } 659 names.add(sharedElementName); 660 View view = sharedElement.first; 661 if (view == null) { 662 throw new IllegalArgumentException("Shared element must not be null"); 663 } 664 views.add(sharedElement.first); 665 } 666 } 667 668 ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names, 669 views, false); 670 opts.mTransitionReceiver = exit; 671 opts.mSharedElementNames = names; 672 opts.mIsReturning = false; 673 opts.mExitCoordinatorIndex = 674 activity.mActivityTransitionState.addExitTransitionCoordinator(exit); 675 return opts; 676 } 677 678 /** @hide */ 679 public static ActivityOptions makeSceneTransitionAnimation(Activity activity, 680 ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames, 681 int resultCode, Intent resultData) { 682 ActivityOptions opts = new ActivityOptions(); 683 opts.mAnimationType = ANIM_SCENE_TRANSITION; 684 opts.mSharedElementNames = sharedElementNames; 685 opts.mTransitionReceiver = exitCoordinator; 686 opts.mIsReturning = true; 687 opts.mResultCode = resultCode; 688 opts.mResultData = resultData; 689 opts.mExitCoordinatorIndex = 690 activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator); 691 return opts; 692 } 693 694 /** 695 * If set along with Intent.FLAG_ACTIVITY_NEW_DOCUMENT then the task being launched will not be 696 * presented to the user but will instead be only available through the recents task list. 697 * In addition, the new task wil be affiliated with the launching activity's task. 698 * Affiliated tasks are grouped together in the recents task list. 699 * 700 * <p>This behavior is not supported for activities with {@link 701 * android.R.styleable#AndroidManifestActivity_launchMode launchMode} values of 702 * <code>singleInstance</code> or <code>singleTask</code>. 703 */ 704 public static ActivityOptions makeTaskLaunchBehind() { 705 final ActivityOptions opts = new ActivityOptions(); 706 opts.mAnimationType = ANIM_LAUNCH_TASK_BEHIND; 707 return opts; 708 } 709 710 /** 711 * Create a basic ActivityOptions that has no special animation associated with it. 712 * Other options can still be set. 713 */ 714 public static ActivityOptions makeBasic() { 715 final ActivityOptions opts = new ActivityOptions(); 716 return opts; 717 } 718 719 /** @hide */ 720 public boolean getLaunchTaskBehind() { 721 return mAnimationType == ANIM_LAUNCH_TASK_BEHIND; 722 } 723 724 private ActivityOptions() { 725 } 726 727 /** @hide */ 728 public ActivityOptions(Bundle opts) { 729 // If the remote side sent us bad parcelables, they won't get the 730 // results they want, which is their loss. 731 opts.setDefusable(true); 732 733 mPackageName = opts.getString(KEY_PACKAGE_NAME); 734 try { 735 mUsageTimeReport = opts.getParcelable(KEY_USAGE_TIME_REPORT); 736 } catch (RuntimeException e) { 737 Slog.w(TAG, e); 738 } 739 mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS); 740 mAnimationType = opts.getInt(KEY_ANIM_TYPE); 741 switch (mAnimationType) { 742 case ANIM_CUSTOM: 743 mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0); 744 mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0); 745 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 746 opts.getBinder(KEY_ANIM_START_LISTENER)); 747 break; 748 749 case ANIM_CUSTOM_IN_PLACE: 750 mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0); 751 break; 752 753 case ANIM_SCALE_UP: 754 case ANIM_CLIP_REVEAL: 755 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 756 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 757 mWidth = opts.getInt(KEY_ANIM_WIDTH, 0); 758 mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0); 759 break; 760 761 case ANIM_THUMBNAIL_SCALE_UP: 762 case ANIM_THUMBNAIL_SCALE_DOWN: 763 case ANIM_THUMBNAIL_ASPECT_SCALE_UP: 764 case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: 765 mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL); 766 mStartX = opts.getInt(KEY_ANIM_START_X, 0); 767 mStartY = opts.getInt(KEY_ANIM_START_Y, 0); 768 mWidth = opts.getInt(KEY_ANIM_WIDTH, 0); 769 mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0); 770 mAnimationStartedListener = IRemoteCallback.Stub.asInterface( 771 opts.getBinder(KEY_ANIM_START_LISTENER)); 772 break; 773 774 case ANIM_SCENE_TRANSITION: 775 mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER); 776 mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false); 777 mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS); 778 mResultData = opts.getParcelable(KEY_RESULT_DATA); 779 mResultCode = opts.getInt(KEY_RESULT_CODE); 780 mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX); 781 break; 782 } 783 mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID); 784 mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1); 785 mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false); 786 mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT); 787 if (opts.containsKey(KEY_ANIM_SPECS)) { 788 Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS); 789 mAnimSpecs = new AppTransitionAnimationSpec[specs.length]; 790 for (int i = specs.length - 1; i >= 0; i--) { 791 mAnimSpecs[i] = (AppTransitionAnimationSpec) specs[i]; 792 } 793 } 794 if (opts.containsKey(KEY_ANIMATION_FINISHED_LISTENER)) { 795 mAnimationFinishedListener = IRemoteCallback.Stub.asInterface( 796 opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER)); 797 } 798 } 799 800 /** 801 * Sets the bounds (window size) that the activity should be launched in. 802 * Rect position should be provided in pixels and in screen coordinates. 803 * Set to null explicitly for fullscreen. 804 * <p> 805 * <strong>NOTE:<strong/> This value is ignored on devices that don't have 806 * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} or 807 * {@link android.content.pm.PackageManager#FEATURE_PICTURE_IN_PICTURE} enabled. 808 * @param screenSpacePixelRect Launch bounds to use for the activity or null for fullscreen. 809 */ 810 public ActivityOptions setLaunchBounds(@Nullable Rect screenSpacePixelRect) { 811 mLaunchBounds = screenSpacePixelRect != null ? new Rect(screenSpacePixelRect) : null; 812 return this; 813 } 814 815 /** @hide */ 816 public String getPackageName() { 817 return mPackageName; 818 } 819 820 /** 821 * Returns the bounds that should be used to launch the activity. 822 * @see #setLaunchBounds(Rect) 823 * @return Bounds used to launch the activity. 824 */ 825 @Nullable 826 public Rect getLaunchBounds() { 827 return mLaunchBounds; 828 } 829 830 /** @hide */ 831 public int getAnimationType() { 832 return mAnimationType; 833 } 834 835 /** @hide */ 836 public int getCustomEnterResId() { 837 return mCustomEnterResId; 838 } 839 840 /** @hide */ 841 public int getCustomExitResId() { 842 return mCustomExitResId; 843 } 844 845 /** @hide */ 846 public int getCustomInPlaceResId() { 847 return mCustomInPlaceResId; 848 } 849 850 /** @hide */ 851 public Bitmap getThumbnail() { 852 return mThumbnail; 853 } 854 855 /** @hide */ 856 public int getStartX() { 857 return mStartX; 858 } 859 860 /** @hide */ 861 public int getStartY() { 862 return mStartY; 863 } 864 865 /** @hide */ 866 public int getWidth() { 867 return mWidth; 868 } 869 870 /** @hide */ 871 public int getHeight() { 872 return mHeight; 873 } 874 875 /** @hide */ 876 public IRemoteCallback getOnAnimationStartListener() { 877 return mAnimationStartedListener; 878 } 879 880 /** @hide */ 881 public IRemoteCallback getAnimationFinishedListener() { 882 return mAnimationFinishedListener; 883 } 884 885 /** @hide */ 886 public int getExitCoordinatorKey() { return mExitCoordinatorIndex; } 887 888 /** @hide */ 889 public void abort() { 890 if (mAnimationStartedListener != null) { 891 try { 892 mAnimationStartedListener.sendResult(null); 893 } catch (RemoteException e) { 894 } 895 } 896 } 897 898 /** @hide */ 899 public boolean isReturning() { 900 return mIsReturning; 901 } 902 903 /** @hide */ 904 public ArrayList<String> getSharedElementNames() { 905 return mSharedElementNames; 906 } 907 908 /** @hide */ 909 public ResultReceiver getResultReceiver() { return mTransitionReceiver; } 910 911 /** @hide */ 912 public int getResultCode() { return mResultCode; } 913 914 /** @hide */ 915 public Intent getResultData() { return mResultData; } 916 917 /** @hide */ 918 public PendingIntent getUsageTimeReport() { 919 return mUsageTimeReport; 920 } 921 922 /** @hide */ 923 public AppTransitionAnimationSpec[] getAnimSpecs() { return mAnimSpecs; } 924 925 /** @hide */ 926 public static ActivityOptions fromBundle(Bundle bOptions) { 927 return bOptions != null ? new ActivityOptions(bOptions) : null; 928 } 929 930 /** @hide */ 931 public static void abort(ActivityOptions options) { 932 if (options != null) { 933 options.abort(); 934 } 935 } 936 937 /** @hide */ 938 public int getLaunchStackId() { 939 return mLaunchStackId; 940 } 941 942 /** @hide */ 943 @TestApi 944 public void setLaunchStackId(int launchStackId) { 945 mLaunchStackId = launchStackId; 946 } 947 948 /** 949 * Sets the task the activity will be launched in. 950 * @hide 951 */ 952 public void setLaunchTaskId(int taskId) { 953 mLaunchTaskId = taskId; 954 } 955 956 /** 957 * @hide 958 */ 959 public int getLaunchTaskId() { 960 return mLaunchTaskId; 961 } 962 963 /** 964 * Set's whether the activity launched with this option should be a task overlay. That is the 965 * activity will always be the top activity of the task and doesn't cause the task to be moved 966 * to the front when it is added. 967 * @hide 968 */ 969 public void setTaskOverlay(boolean taskOverlay) { 970 mTaskOverlay = taskOverlay; 971 } 972 973 /** 974 * @hide 975 */ 976 public boolean getTaskOverlay() { 977 return mTaskOverlay; 978 } 979 980 /** @hide */ 981 public int getDockCreateMode() { 982 return mDockCreateMode; 983 } 984 985 /** @hide */ 986 public void setDockCreateMode(int dockCreateMode) { 987 mDockCreateMode = dockCreateMode; 988 } 989 990 /** 991 * Update the current values in this ActivityOptions from those supplied 992 * in <var>otherOptions</var>. Any values 993 * defined in <var>otherOptions</var> replace those in the base options. 994 */ 995 public void update(ActivityOptions otherOptions) { 996 if (otherOptions.mPackageName != null) { 997 mPackageName = otherOptions.mPackageName; 998 } 999 mUsageTimeReport = otherOptions.mUsageTimeReport; 1000 mTransitionReceiver = null; 1001 mSharedElementNames = null; 1002 mIsReturning = false; 1003 mResultData = null; 1004 mResultCode = 0; 1005 mExitCoordinatorIndex = 0; 1006 mAnimationType = otherOptions.mAnimationType; 1007 switch (otherOptions.mAnimationType) { 1008 case ANIM_CUSTOM: 1009 mCustomEnterResId = otherOptions.mCustomEnterResId; 1010 mCustomExitResId = otherOptions.mCustomExitResId; 1011 mThumbnail = null; 1012 if (mAnimationStartedListener != null) { 1013 try { 1014 mAnimationStartedListener.sendResult(null); 1015 } catch (RemoteException e) { 1016 } 1017 } 1018 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 1019 break; 1020 case ANIM_CUSTOM_IN_PLACE: 1021 mCustomInPlaceResId = otherOptions.mCustomInPlaceResId; 1022 break; 1023 case ANIM_SCALE_UP: 1024 mStartX = otherOptions.mStartX; 1025 mStartY = otherOptions.mStartY; 1026 mWidth = otherOptions.mWidth; 1027 mHeight = otherOptions.mHeight; 1028 if (mAnimationStartedListener != null) { 1029 try { 1030 mAnimationStartedListener.sendResult(null); 1031 } catch (RemoteException e) { 1032 } 1033 } 1034 mAnimationStartedListener = null; 1035 break; 1036 case ANIM_THUMBNAIL_SCALE_UP: 1037 case ANIM_THUMBNAIL_SCALE_DOWN: 1038 case ANIM_THUMBNAIL_ASPECT_SCALE_UP: 1039 case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: 1040 mThumbnail = otherOptions.mThumbnail; 1041 mStartX = otherOptions.mStartX; 1042 mStartY = otherOptions.mStartY; 1043 mWidth = otherOptions.mWidth; 1044 mHeight = otherOptions.mHeight; 1045 if (mAnimationStartedListener != null) { 1046 try { 1047 mAnimationStartedListener.sendResult(null); 1048 } catch (RemoteException e) { 1049 } 1050 } 1051 mAnimationStartedListener = otherOptions.mAnimationStartedListener; 1052 break; 1053 case ANIM_SCENE_TRANSITION: 1054 mTransitionReceiver = otherOptions.mTransitionReceiver; 1055 mSharedElementNames = otherOptions.mSharedElementNames; 1056 mIsReturning = otherOptions.mIsReturning; 1057 mThumbnail = null; 1058 mAnimationStartedListener = null; 1059 mResultData = otherOptions.mResultData; 1060 mResultCode = otherOptions.mResultCode; 1061 mExitCoordinatorIndex = otherOptions.mExitCoordinatorIndex; 1062 break; 1063 } 1064 mAnimSpecs = otherOptions.mAnimSpecs; 1065 mAnimationFinishedListener = otherOptions.mAnimationFinishedListener; 1066 } 1067 1068 /** 1069 * Returns the created options as a Bundle, which can be passed to 1070 * {@link android.content.Context#startActivity(android.content.Intent, android.os.Bundle) 1071 * Context.startActivity(Intent, Bundle)} and related methods. 1072 * Note that the returned Bundle is still owned by the ActivityOptions 1073 * object; you must not modify it, but can supply it to the startActivity 1074 * methods that take an options Bundle. 1075 */ 1076 public Bundle toBundle() { 1077 if (mAnimationType == ANIM_DEFAULT) { 1078 return null; 1079 } 1080 Bundle b = new Bundle(); 1081 if (mPackageName != null) { 1082 b.putString(KEY_PACKAGE_NAME, mPackageName); 1083 } 1084 if (mLaunchBounds != null) { 1085 b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds); 1086 } 1087 b.putInt(KEY_ANIM_TYPE, mAnimationType); 1088 if (mUsageTimeReport != null) { 1089 b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport); 1090 } 1091 switch (mAnimationType) { 1092 case ANIM_CUSTOM: 1093 b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId); 1094 b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId); 1095 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 1096 != null ? mAnimationStartedListener.asBinder() : null); 1097 break; 1098 case ANIM_CUSTOM_IN_PLACE: 1099 b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId); 1100 break; 1101 case ANIM_SCALE_UP: 1102 case ANIM_CLIP_REVEAL: 1103 b.putInt(KEY_ANIM_START_X, mStartX); 1104 b.putInt(KEY_ANIM_START_Y, mStartY); 1105 b.putInt(KEY_ANIM_WIDTH, mWidth); 1106 b.putInt(KEY_ANIM_HEIGHT, mHeight); 1107 break; 1108 case ANIM_THUMBNAIL_SCALE_UP: 1109 case ANIM_THUMBNAIL_SCALE_DOWN: 1110 case ANIM_THUMBNAIL_ASPECT_SCALE_UP: 1111 case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: 1112 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail); 1113 b.putInt(KEY_ANIM_START_X, mStartX); 1114 b.putInt(KEY_ANIM_START_Y, mStartY); 1115 b.putInt(KEY_ANIM_WIDTH, mWidth); 1116 b.putInt(KEY_ANIM_HEIGHT, mHeight); 1117 b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener 1118 != null ? mAnimationStartedListener.asBinder() : null); 1119 break; 1120 case ANIM_SCENE_TRANSITION: 1121 if (mTransitionReceiver != null) { 1122 b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver); 1123 } 1124 b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning); 1125 b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames); 1126 b.putParcelable(KEY_RESULT_DATA, mResultData); 1127 b.putInt(KEY_RESULT_CODE, mResultCode); 1128 b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex); 1129 break; 1130 } 1131 b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId); 1132 b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId); 1133 b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay); 1134 b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode); 1135 if (mAnimSpecs != null) { 1136 b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs); 1137 } 1138 if (mAnimationFinishedListener != null) { 1139 b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder()); 1140 } 1141 1142 return b; 1143 } 1144 1145 /** 1146 * Ask the the system track that time the user spends in the app being launched, and 1147 * report it back once done. The report will be sent to the given receiver, with 1148 * the extras {@link #EXTRA_USAGE_TIME_REPORT} and {@link #EXTRA_USAGE_TIME_REPORT_PACKAGES} 1149 * filled in. 1150 * 1151 * <p>The time interval tracked is from launching this activity until the user leaves 1152 * that activity's flow. They are considered to stay in the flow as long as 1153 * new activities are being launched or returned to from the original flow, 1154 * even if this crosses package or task boundaries. For example, if the originator 1155 * starts an activity to view an image, and while there the user selects to share, 1156 * which launches their email app in a new task, and they complete the share, the 1157 * time during that entire operation will be included until they finally hit back from 1158 * the original image viewer activity.</p> 1159 * 1160 * <p>The user is considered to complete a flow once they switch to another 1161 * activity that is not part of the tracked flow. This may happen, for example, by 1162 * using the notification shade, launcher, or recents to launch or switch to another 1163 * app. Simply going in to these navigation elements does not break the flow (although 1164 * the launcher and recents stops time tracking of the session); it is the act of 1165 * going somewhere else that completes the tracking.</p> 1166 * 1167 * @param receiver A broadcast receiver that willl receive the report. 1168 */ 1169 public void requestUsageTimeReport(PendingIntent receiver) { 1170 mUsageTimeReport = receiver; 1171 } 1172 1173 /** 1174 * Return the filtered options only meant to be seen by the target activity itself 1175 * @hide 1176 */ 1177 public ActivityOptions forTargetActivity() { 1178 if (mAnimationType == ANIM_SCENE_TRANSITION) { 1179 final ActivityOptions result = new ActivityOptions(); 1180 result.update(this); 1181 return result; 1182 } 1183 1184 return null; 1185 } 1186 1187 /** @hide */ 1188 @Override 1189 public String toString() { 1190 return "ActivityOptions(" + hashCode() + "), mPackageName=" + mPackageName 1191 + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY=" 1192 + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight; 1193 } 1194 } 1195