1 /* 2 * Copyright (C) 2020 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 com.google.android.setupdesign.transition; 18 19 import android.annotation.TargetApi; 20 import android.app.Activity; 21 import android.app.ActivityOptions; 22 import android.app.Fragment; 23 import android.content.ActivityNotFoundException; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.res.TypedArray; 27 import android.os.Build.VERSION_CODES; 28 import android.os.Bundle; 29 import android.util.Log; 30 import androidx.annotation.IntDef; 31 import androidx.annotation.Nullable; 32 import androidx.annotation.VisibleForTesting; 33 import com.google.android.setupcompat.partnerconfig.PartnerConfig; 34 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper; 35 import com.google.android.setupcompat.util.BuildCompatUtils; 36 import com.google.android.setupdesign.R; 37 import com.google.android.setupdesign.util.ThemeHelper; 38 import com.google.errorprone.annotations.InlineMe; 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 42 /** Helper class for apply the transition to the pages which uses platform version. */ 43 public class TransitionHelper { 44 45 private static final String TAG = "TransitionHelper"; 46 47 /* 48 * In Setup Wizard, all Just-a-sec style screens (i.e. screens that has an indeterminate 49 * progress bar and automatically finishes itself), should do a cross-fade when entering or 50 * exiting the screen. For all other screens, the transition should be a slide-in-from-right 51 * or customized. 52 * 53 * We use two different ways to override the transitions. The first is calling 54 * overridePendingTransition in code, and the second is using windowAnimationStyle in the theme. 55 * They have the following priority when framework is figuring out what transition to use: 56 * 1. overridePendingTransition, entering activity (highest priority) 57 * 2. overridePendingTransition, exiting activity 58 * 3. windowAnimationStyle, entering activity 59 * 4. windowAnimationStyle, exiting activity 60 * 61 * This is why, in general, overridePendingTransition is used to specify the fade animation, 62 * while windowAnimationStyle is used to specify the slide transition. This way fade animation 63 * will take priority over the slide animation. 64 * 65 * Below are types of animation when switching activities. These are return values for 66 * {@link #getTransition()}. Each of these values represents 4 animations: (backward exit, 67 * backward enter, forward exit, forward enter). 68 * 69 * We override the transition in the following flow 70 * +--------------+-------------------------+--------------------------+ 71 * | | going forward | going backward | 72 * +--------------+-------------------------+--------------------------+ 73 * | old activity | startActivity(OnResult) | onActivityResult | 74 * +--------------+-------------------------+--------------------------+ 75 * | new activity | onStart | finish (RESULT_CANCELED) | 76 * +--------------+-------------------------+--------------------------+ 77 */ 78 79 /** The constant of transition type. */ 80 @Retention(RetentionPolicy.SOURCE) 81 @IntDef({ 82 TRANSITION_NONE, 83 TRANSITION_NO_OVERRIDE, 84 TRANSITION_FRAMEWORK_DEFAULT, 85 TRANSITION_SLIDE, 86 TRANSITION_FADE, 87 TRANSITION_FRAMEWORK_DEFAULT_PRE_P, 88 TRANSITION_CAPTIVE, 89 }) 90 public @interface TransitionType {} 91 92 /** No transition, as in overridePendingTransition(0, 0). */ 93 public static final int TRANSITION_NONE = -1; 94 95 /** 96 * No override. If this is specified as the transition, overridePendingTransition will not be 97 * called. 98 */ 99 public static final int TRANSITION_NO_OVERRIDE = 0; 100 101 /** 102 * Override the transition to the framework default. This values are read from {@link 103 * android.R.style#Animation_Activity}. 104 */ 105 public static final int TRANSITION_FRAMEWORK_DEFAULT = 1; 106 107 /** Override the transition to a slide-in-from-right (or from-left for RTL locales). */ 108 public static final int TRANSITION_SLIDE = 2; 109 110 /** 111 * Override the transition to fade in the new activity, while keeping the old activity. Setup 112 * wizard does not use cross fade to avoid the bright-dim-bright effect when transitioning between 113 * two screens that look similar. 114 */ 115 public static final int TRANSITION_FADE = 3; 116 117 /** Override the transition to the old framework default pre P. */ 118 public static final int TRANSITION_FRAMEWORK_DEFAULT_PRE_P = 4; 119 120 /** 121 * Override the transition to the specific transition and the transition type will depends on the 122 * partner resource. 123 */ 124 // TODO: Add new partner resource to determine which transition type would be apply. 125 public static final int TRANSITION_CAPTIVE = 5; 126 127 /** Override the transition to a fade-through-from-right (or from-left for RTL locales). */ 128 public static final int TRANSITION_FADE_THROUGH = 6; 129 130 /** 131 * No override. If this is specified as the transition, the enter/exit transition of the window 132 * will not be set and keep original behavior. 133 */ 134 public static final int CONFIG_TRANSITION_NONE = 0; 135 136 /** Override the transition to the specific type that will depend on the partner resource. */ 137 public static final int CONFIG_TRANSITION_SHARED_X_AXIS = 1; 138 139 /** 140 * Passed in an intent as EXTRA_ACTIVITY_OPTIONS. This is the {@link ActivityOptions} of the 141 * transition used in {@link Activity#startActivity} or {@link Activity#startActivityForResult}. 142 * 143 * @deprecated Deprecated to use CONFIG_TRANSITION_SHARED_X_AXIS transition, so it never have 144 * activity options input. 145 */ 146 @Deprecated public static final String EXTRA_ACTIVITY_OPTIONS = "sud:activity_options"; 147 148 /** A flag to avoid the {@link Activity#finish} been called more than once. */ 149 @VisibleForTesting static boolean isFinishCalled = false; 150 151 /** A flag to avoid the {@link Activity#startActivity} called more than once. */ 152 @VisibleForTesting static boolean isStartActivity = false; 153 154 /** A flag to avoid the {@link Activity#startActivityForResult} called more than once. */ 155 @VisibleForTesting static boolean isStartActivityForResult = false; 156 TransitionHelper()157 private TransitionHelper() {} 158 159 /** 160 * Apply the transition for going forward which is decided by {@code Animation.SudWindowAnimation} 161 * theme if the API level is equal or higher than {@link android.os.Build.VERSION_CODES#U}. 162 * 163 * <p>Otherwise, apply the transition for going forward which is decided by partner resource 164 * {@link PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code 165 * setupwizard.transition_type} if the API level is equal or lower than {@link 166 * android.os.Build.VERSION_CODES#T}. The default transition that will be applied is {@link 167 * #TRANSITION_SLIDE}. 168 * 169 * <p>The timing to apply the transition is going forward from the previous activity to this, or 170 * going forward from this activity to the next. 171 * 172 * <p>For example, in the flow below, the forward transitions will be applied to all arrows 173 * pointing to the right. Previous screen --> This screen --> Next screen 174 */ 175 @TargetApi(VERSION_CODES.LOLLIPOP) applyForwardTransition(Activity activity)176 public static void applyForwardTransition(Activity activity) { 177 applyForwardTransition(activity, TRANSITION_CAPTIVE); 178 } 179 180 /** 181 * Apply the transition for going forward which is decided by partner resource {@link 182 * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}. 183 * The default transition that will be applied is {@link #CONFIG_TRANSITION_NONE}. The timing to 184 * apply the transition is going forward from the previous {@link Fragment} to this, or going 185 * forward from this {@link Fragment} to the next. 186 * 187 * @deprecated Deprecated to use CONFIG_TRANSITION_SHARED_X_AXIS transition, so it never have 188 * activity options input, should start the activity directly. 189 */ 190 @TargetApi(VERSION_CODES.M) 191 @Deprecated applyForwardTransition(Fragment fragment)192 public static void applyForwardTransition(Fragment fragment) { 193 // Do nothing 194 } 195 196 /** 197 * Apply the transition for going forward which is decided by {@code Animation.SudWindowAnimation} 198 * theme if the API level is equal or higher than {@link android.os.Build.VERSION_CODES#U}. 199 * 200 * <p>Otherwise, apply the transition for going forward which is decided by the argument {@code 201 * transitionId} if the API level is equal or lower than {@link android.os.Build.VERSION_CODES#T}. 202 * 203 * <p>The timing to apply the transition is going forward from the previous activity to this, or 204 * going forward from this activity to the next. 205 */ 206 @TargetApi(VERSION_CODES.LOLLIPOP) applyForwardTransition(Activity activity, @TransitionType int transitionId)207 public static void applyForwardTransition(Activity activity, @TransitionType int transitionId) { 208 applyForwardTransition(activity, transitionId, /* useClientTransitionSettings= */ false); 209 } 210 211 /** 212 * Apply the transition for going forward which is decided by {@code Animation.SudWindowAnimation} 213 * theme if the API level is equal or higher than {@link android.os.Build.VERSION_CODES#U}, and 214 * argument {@code useClientTransitionSettings} is false, and System property {@code 215 * suw_apply_glif_theme_controlled_transition} is true, and {@code TRANSITION_FADE_THOUGH} 216 * transition is not specified. 217 * 218 * <p>Otherwise, apply the transition for going forward which is decided by the argument {@code 219 * transitionId}, {@code shared_x_axis_activity} transition is used only when {@code 220 * TRANSITION_FADE_TROUGH} transition is specified, and System property {@code * 221 * suw_apply_glif_theme_controlled_transition} is true, and the API level is equal or more than 222 * {@link android.os.Build.VERSION_CODES#U}, other {@code transitionId} can be specified if the 223 * API level is equal or lower than {@link android.os.Build.VERSION_CODES#T}, or argument {@code 224 * useClientTransitionSettings} is true, or System property {@code 225 * suw_apply_glif_theme_controlled_transition} is false. The default transition that will be 226 * applied is {@link #TRANSITION_SLIDE}. 227 * 228 * <p>The timing to apply the transition is going forward from the previous activity to this, or 229 * going forward from this activity to the next. 230 * 231 * <p>For example, in the flow below, the forward transitions will be applied to all arrows 232 * pointing to the right. Previous screen --> This screen --> Next screen 233 */ 234 @TargetApi(VERSION_CODES.LOLLIPOP) applyForwardTransition( Activity activity, @TransitionType int transitionId, boolean useClientTransitionSettings)235 public static void applyForwardTransition( 236 Activity activity, @TransitionType int transitionId, boolean useClientTransitionSettings) { 237 if (BuildCompatUtils.isAtLeastU() 238 && !useClientTransitionSettings 239 && PartnerConfigHelper.isGlifThemeControlledTransitionApplied(activity) 240 && transitionId != TRANSITION_FADE_THROUGH) { 241 // Do nothing 242 } else if (BuildCompatUtils.isAtLeastU() && transitionId == TRANSITION_FADE_THROUGH) { 243 if (PartnerConfigHelper.isGlifThemeControlledTransitionApplied(activity)) { 244 int openEnterTransition = 245 ThemeHelper.shouldApplyDynamicColor(activity) 246 ? R.anim.shared_x_axis_activity_open_enter_dynamic_color 247 : R.anim.shared_x_axis_activity_open_enter; 248 activity.overridePendingTransition( 249 openEnterTransition, R.anim.shared_x_axis_activity_open_exit); 250 } else { 251 activity.overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); 252 } 253 } else if (transitionId == TRANSITION_SLIDE) { 254 activity.overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out); 255 } else if (transitionId == TRANSITION_FADE) { 256 activity.overridePendingTransition(android.R.anim.fade_in, R.anim.sud_stay); 257 } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT) { 258 TypedArray typedArray = 259 activity.obtainStyledAttributes( 260 android.R.style.Animation_Activity, 261 new int[] { 262 android.R.attr.activityOpenEnterAnimation, android.R.attr.activityOpenExitAnimation 263 }); 264 activity.overridePendingTransition( 265 typedArray.getResourceId(/* index= */ 0, /* defValue= */ 0), 266 typedArray.getResourceId(/* index= */ 1, /* defValue= */ 0)); 267 typedArray.recycle(); 268 } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT_PRE_P) { 269 activity.overridePendingTransition( 270 R.anim.sud_pre_p_activity_open_enter, R.anim.sud_pre_p_activity_open_exit); 271 } else if (transitionId == TRANSITION_NONE) { 272 // For TRANSITION_NONE, turn off the transition 273 activity.overridePendingTransition(/* enterAnim= */ 0, /* exitAnim= */ 0); 274 } 275 // For TRANSITION_NO_OVERRIDE or other values, do not override the transition 276 } 277 278 /** 279 * Apply the transition for going backward which is decided by {@code 280 * Animation.SudWindowAnimation} theme if the API level is equal or higher than {@link 281 * android.os.Build.VERSION_CODES#U}. 282 * 283 * <p>Otherwise, apply the transition for going backward which is decided by partner resource 284 * {@link PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code 285 * setupwizard.transition_type} if the API level is equal or lower than {@link 286 * android.os.Build.VERSION_CODES#T}. The default transition that will be applied is {@link 287 * #TRANSITION_SLIDE}. 288 * 289 * <p>The timing to apply the transition is going backward from the next activity to this, or 290 * going backward from this activity to the previous. 291 * 292 * <p>For example, in the flow below, the backward transitions will be applied to all arrows 293 * pointing to the left. Previous screen <-- This screen <-- Next screen. 294 */ 295 @TargetApi(VERSION_CODES.LOLLIPOP) applyBackwardTransition(Activity activity)296 public static void applyBackwardTransition(Activity activity) { 297 applyBackwardTransition(activity, TRANSITION_CAPTIVE); 298 } 299 300 /** 301 * Apply the transition for going backward which is decided by partner resource {@link 302 * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}. 303 * The default transition that will be applied is {@link #CONFIG_TRANSITION_NONE}. The timing to 304 * apply the transition is going backward from the next {@link Fragment} to this, or going 305 * backward from this {@link Fragment} to the previous. 306 * 307 * @deprecated Deprecated to use CONFIG_TRANSITION_SHARED_X_AXIS transition, so it never have 308 * activity options input, should start the activity directly. 309 */ 310 @TargetApi(VERSION_CODES.M) 311 @Deprecated applyBackwardTransition(Fragment fragment)312 public static void applyBackwardTransition(Fragment fragment) { 313 // Do nothing 314 } 315 316 /** 317 * Apply the transition for going backward which is decided by {@code 318 * Animation.SudWindowAnimation} theme if the API level is equal or higher than {@link 319 * android.os.Build.VERSION_CODES#U}. 320 * 321 * <p>Otherwise, apply the transition for going backward which is decided by the argument {@code 322 * transitionId} if the API level is equal or lower than {@link android.os.Build.VERSION_CODES#T}. 323 * 324 * <p>The timing to apply the transition is going backward from the next activity to this, or 325 * going backward from this activity to the previous. 326 */ 327 @TargetApi(VERSION_CODES.LOLLIPOP) applyBackwardTransition(Activity activity, @TransitionType int transitionId)328 public static void applyBackwardTransition(Activity activity, @TransitionType int transitionId) { 329 applyBackwardTransition(activity, transitionId, /* useClientTransitionSettings= */ false); 330 } 331 332 /** 333 * Apply the transition for going backward which is decided by {@code 334 * Animation.SudWindowAnimation} theme if the API level is equal or higher than {@link 335 * android.os.Build.VERSION_CODES#U}, and argument {@code useClientTransitionSettings} is false, 336 * and System property {@code suw_apply_glif_theme_controlled_transition} is true, and {@code 337 * TRANSITION_FADE_THOUGH} transition is not specified. 338 * 339 * <p>Otherwise, apply the transition for going backward which is decided by the argument {@code 340 * transitionId}, {@code shared_x_axis_activity} transition is used only when {@code 341 * TRANSITION_FADE_TROUGH} transition is specified, and System property {@code * 342 * suw_apply_glif_theme_controlled_transition} is true, and the API level is equal or more than 343 * {@link android.os.Build.VERSION_CODES#U}, other {@code transitionId} can be specified if the 344 * API level is equal or lower than {@link android.os.Build.VERSION_CODES#T}, or argument {@code 345 * useClientTransitionSettings} is true, or System property {@code 346 * suw_apply_glif_theme_controlled_transition} is false. The default transition that will be 347 * applied is {@link #TRANSITION_SLIDE}. 348 * 349 * <p>The timing to apply the transition is going backward from the next activity to this, or 350 * going backward from this activity to the previous. 351 * 352 * <p>For example, in the flow below, the backward transitions will be applied to all arrows 353 * pointing to the left. Previous screen <-- This screen <-- Next screen 354 */ 355 @TargetApi(VERSION_CODES.LOLLIPOP) applyBackwardTransition( Activity activity, @TransitionType int transitionId, boolean useClientTransitionSettings)356 public static void applyBackwardTransition( 357 Activity activity, @TransitionType int transitionId, boolean useClientTransitionSettings) { 358 if (BuildCompatUtils.isAtLeastU() 359 && !useClientTransitionSettings 360 && PartnerConfigHelper.isGlifThemeControlledTransitionApplied(activity) 361 && transitionId != TRANSITION_FADE_THROUGH) { 362 // Do nothing 363 } else if (BuildCompatUtils.isAtLeastU() && transitionId == TRANSITION_FADE_THROUGH) { 364 if (PartnerConfigHelper.isGlifThemeControlledTransitionApplied(activity)) { 365 int closeEnterTransition = 366 ThemeHelper.shouldApplyDynamicColor(activity) 367 ? R.anim.shared_x_axis_activity_close_enter_dynamic_color 368 : R.anim.shared_x_axis_activity_close_enter; 369 activity.overridePendingTransition( 370 closeEnterTransition, R.anim.shared_x_axis_activity_close_exit); 371 } else { 372 activity.overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out); 373 } 374 } else if (transitionId == TRANSITION_SLIDE) { 375 activity.overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out); 376 } else if (transitionId == TRANSITION_FADE) { 377 activity.overridePendingTransition(R.anim.sud_stay, android.R.anim.fade_out); 378 } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT) { 379 TypedArray typedArray = 380 activity.obtainStyledAttributes( 381 android.R.style.Animation_Activity, 382 new int[] { 383 android.R.attr.activityCloseEnterAnimation, 384 android.R.attr.activityCloseExitAnimation 385 }); 386 activity.overridePendingTransition( 387 typedArray.getResourceId(/* index= */ 0, /* defValue= */ 0), 388 typedArray.getResourceId(/* index= */ 1, /* defValue= */ 0)); 389 typedArray.recycle(); 390 } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT_PRE_P) { 391 activity.overridePendingTransition( 392 R.anim.sud_pre_p_activity_close_enter, R.anim.sud_pre_p_activity_close_exit); 393 } else if (transitionId == TRANSITION_NONE) { 394 // For TRANSITION_NONE, turn off the transition 395 activity.overridePendingTransition(/* enterAnim= */ 0, /* exitAnim= */ 0); 396 } 397 } 398 399 /** 400 * A wrapper method, create an {@link android.app.ActivityOptions} to transition between 401 * activities as the {@link ActivityOptions} parameter of {@link Activity#startActivity}. 402 * 403 * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null. 404 * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run 405 * the given Intent. 406 * @deprecated Deprecated to use CONFIG_TRANSITION_SHARED_X_AXIS transition, so it never have 407 * activity options input, should start the activity directly. 408 */ 409 @InlineMe(replacement = "activity.startActivity(intent)") 410 @Deprecated startActivityWithTransition(Activity activity, Intent intent)411 public static void startActivityWithTransition(Activity activity, Intent intent) { 412 activity.startActivity(intent); 413 } 414 415 /** 416 * A wrapper method, create an {@link android.app.ActivityOptions} to transition between 417 * activities as the {@link ActivityOptions} parameter of {@link Activity#startActivity}. 418 * 419 * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null. 420 * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run 421 * the given Intent. 422 * @deprecated Deprecated to use CONFIG_TRANSITION_SHARED_X_AXIS transition, so it never have 423 * activity options input, should start the activity directly. 424 */ 425 @Deprecated startActivityWithTransition( Activity activity, Intent intent, Bundle overrideActivityOptions)426 public static void startActivityWithTransition( 427 Activity activity, Intent intent, Bundle overrideActivityOptions) { 428 if (activity == null) { 429 throw new IllegalArgumentException("Invalid activity=" + activity); 430 } 431 432 if (intent == null) { 433 throw new IllegalArgumentException("Invalid intent=" + intent); 434 } 435 436 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == Intent.FLAG_ACTIVITY_NEW_TASK) { 437 Log.e( 438 TAG, 439 "The transition won't take effect since the WindowManager does not allow override new" 440 + " task transitions"); 441 } 442 443 if (!isStartActivity) { 444 isStartActivity = true; 445 activity.startActivity(intent); 446 } 447 isStartActivity = false; 448 } 449 450 /** 451 * A wrapper method, create an {@link android.app.ActivityOptions} to transition between 452 * activities as the {@code activityOptions} parameter of {@link Activity#startActivityForResult}. 453 * 454 * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null. 455 * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run 456 * the given Intent. 457 * @deprecated Deprecated to use CONFIG_TRANSITION_SHARED_X_AXIS transition, so it never have 458 * activity options input, should start the activity directly. 459 */ 460 @InlineMe(replacement = "activity.startActivityForResult(intent, requestCode)") 461 @Deprecated startActivityForResultWithTransition( Activity activity, Intent intent, int requestCode)462 public static void startActivityForResultWithTransition( 463 Activity activity, Intent intent, int requestCode) { 464 activity.startActivityForResult(intent, requestCode); 465 } 466 467 /** 468 * A wrapper method, create an {@link android.app.ActivityOptions} to transition between 469 * activities as the {@code activityOptions} parameter of {@link Activity#startActivityForResult}. 470 * 471 * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null. 472 * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run 473 * the given Intent. 474 * @deprecated Deprecated to use CONFIG_TRANSITION_SHARED_X_AXIS transition, so it never have 475 * activity options input, should start the activity directly. 476 */ 477 @Deprecated startActivityForResultWithTransition( Activity activity, Intent intent, int requestCode, Bundle overrideActivityOptions)478 public static void startActivityForResultWithTransition( 479 Activity activity, Intent intent, int requestCode, Bundle overrideActivityOptions) { 480 if (activity == null) { 481 throw new IllegalArgumentException("Invalid activity=" + activity); 482 } 483 484 if (intent == null) { 485 throw new IllegalArgumentException("Invalid intent=" + intent); 486 } 487 488 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == Intent.FLAG_ACTIVITY_NEW_TASK) { 489 Log.e( 490 TAG, 491 "The transition won't take effect since the WindowManager does not allow override new" 492 + " task transitions"); 493 } 494 495 if (!isStartActivityForResult) { 496 try { 497 isStartActivityForResult = true; 498 activity.startActivityForResult(intent, requestCode); 499 } catch (ActivityNotFoundException e) { 500 Log.w(TAG, "Activity not found when startActivityForResult with transition."); 501 throw e; 502 } finally { 503 // Allow to start next activity. 504 isStartActivityForResult = false; 505 } 506 } 507 } 508 509 /** 510 * A wrapper method, calling {@link Activity#finishAfterTransition()} to trigger exit transition 511 * when running in Android S and the transition type {link #CONFIG_TRANSITION_SHARED_X_AXIS}. 512 * 513 * @throws IllegalArgumentException is thrown when {@code activity} is null. 514 */ finishActivity(Activity activity)515 public static void finishActivity(Activity activity) { 516 if (activity == null) { 517 throw new IllegalArgumentException("Invalid activity=" + activity); 518 } 519 520 // Avoids finish been called more than once. 521 if (!isFinishCalled) { 522 isFinishCalled = true; 523 Log.w( 524 TAG, 525 "Fallback to using Activity#finish() due to the" 526 + " Activity#finishAfterTransition() is supported from Android Sdk " 527 + VERSION_CODES.LOLLIPOP); 528 activity.finish(); 529 } 530 isFinishCalled = false; 531 } 532 533 /** 534 * Returns the transition type from the {@link PartnerConfig#CONFIG_TRANSITION_TYPE} partner 535 * resource on Android S, otherwise returns {@link #CONFIG_TRANSITION_NONE}. 536 */ getConfigTransitionType(Context context)537 public static int getConfigTransitionType(Context context) { 538 return BuildCompatUtils.isAtLeastS() && ThemeHelper.shouldApplyExtendedPartnerConfig(context) 539 ? PartnerConfigHelper.get(context) 540 .getInteger(context, PartnerConfig.CONFIG_TRANSITION_TYPE, CONFIG_TRANSITION_NONE) 541 : CONFIG_TRANSITION_NONE; 542 } 543 544 /** 545 * A wrapper method, create a {@link Bundle} from {@link ActivityOptions} to transition between 546 * Activities using cross-Activity scene animations. This {@link Bundle} that can be used with 547 * {@link Context#startActivity(Intent, Bundle)} and related methods. 548 * 549 * <p>Example usage: 550 * 551 * <pre>{@code 552 * Intent intent = new Intent("com.example.NEXT_ACTIVITY"); 553 * activity.startActivity(intent, TransitionHelper.makeActivityOptions(activity, intent, null); 554 * }</pre> 555 * 556 * <p>Unexpected usage: 557 * 558 * <pre>{@code 559 * Intent intent = new Intent("com.example.NEXT_ACTIVITY"); 560 * Intent intent2 = new Intent("com.example.NEXT_ACTIVITY"); 561 * activity.startActivity(intent, TransitionHelper.makeActivityOptions(activity, intent2, null); 562 * }</pre> 563 * 564 * @deprecated Deprecated to use CONFIG_TRANSITION_SHARED_X_AXIS transition, so it never have 565 * activity options input, should start the activity directly. 566 */ 567 @InlineMe(replacement = "null") 568 @Nullable 569 @Deprecated makeActivityOptions(Activity activity, Intent intent)570 public static Bundle makeActivityOptions(Activity activity, Intent intent) { 571 return null; 572 } 573 574 /** 575 * A wrapper method, create a {@link Bundle} from {@link ActivityOptions} to transition between 576 * Activities using cross-Activity scene animations. This {@link Bundle} that can be used with 577 * {@link Context#startActivity(Intent, Bundle)} and related methods. When this {@code activity} 578 * is a no UI activity(the activity doesn't inflate any layouts), you will need to pass the bundle 579 * coming from previous UI activity as the {@link ActivityOptions}, otherwise, the transition 580 * won't be take effect. The {@code overrideActivityOptionsFromIntent} is supporting this purpose 581 * to return the {@link ActivityOptions} instead of creating from this no UI activity while the 582 * transition is apply {@link #CONFIG_TRANSITION_SHARED_X_AXIS} config. Moreover, the 583 * startActivity*WithTransition relative methods and {@link #makeActivityOptions} will put {@link 584 * ActivityOptions} to the {@code intent} by default, you can get the {@link ActivityOptions} 585 * which makes from previous activity by accessing {@link #EXTRA_ACTIVITY_OPTIONS} extra from 586 * {@link Activity#getIntent()}. 587 * 588 * <p>Example usage of a no UI activity: 589 * 590 * <pre>{@code 591 * Intent intent = new Intent("com.example.NEXT_ACTIVITY"); 592 * activity.startActivity(intent, TransitionHelper.makeActivityOptions(activity, intent, true); 593 * }</pre> 594 * 595 * @deprecated Deprecated to use CONFIG_TRANSITION_SHARED_X_AXIS transition, so it never have 596 * activity options input, should start the activity directly. 597 */ 598 @InlineMe(replacement = "null") 599 @Nullable 600 @Deprecated makeActivityOptions( Activity activity, Intent intent, boolean overrideActivityOptionsFromIntent)601 public static Bundle makeActivityOptions( 602 Activity activity, Intent intent, boolean overrideActivityOptionsFromIntent) { 603 return null; 604 } 605 } 606