FragmentManager.java revision 805fb8e2508edfc45a1b80a3bf63501aa3507bf2
1/* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package androidx.fragment.app; 18 19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21import android.animation.Animator; 22import android.animation.AnimatorInflater; 23import android.animation.AnimatorListenerAdapter; 24import android.animation.AnimatorSet; 25import android.animation.PropertyValuesHolder; 26import android.animation.ValueAnimator; 27import android.content.Context; 28import android.content.res.Configuration; 29import android.content.res.Resources.NotFoundException; 30import android.content.res.TypedArray; 31import android.os.Build; 32import android.os.Bundle; 33import android.os.Looper; 34import android.os.Parcel; 35import android.os.Parcelable; 36import android.util.AttributeSet; 37import android.util.Log; 38import android.util.SparseArray; 39import android.view.LayoutInflater; 40import android.view.Menu; 41import android.view.MenuInflater; 42import android.view.MenuItem; 43import android.view.View; 44import android.view.ViewGroup; 45import android.view.animation.AccelerateInterpolator; 46import android.view.animation.AlphaAnimation; 47import android.view.animation.Animation; 48import android.view.animation.Animation.AnimationListener; 49import android.view.animation.AnimationSet; 50import android.view.animation.AnimationUtils; 51import android.view.animation.DecelerateInterpolator; 52import android.view.animation.Interpolator; 53import android.view.animation.ScaleAnimation; 54import android.view.animation.Transformation; 55 56import androidx.annotation.CallSuper; 57import androidx.annotation.IdRes; 58import androidx.annotation.NonNull; 59import androidx.annotation.Nullable; 60import androidx.annotation.RestrictTo; 61import androidx.annotation.StringRes; 62import androidx.collection.ArraySet; 63import androidx.core.util.DebugUtils; 64import androidx.core.util.LogWriter; 65import androidx.core.view.ViewCompat; 66import androidx.lifecycle.ViewModelStore; 67 68import java.io.FileDescriptor; 69import java.io.PrintWriter; 70import java.lang.reflect.Field; 71import java.util.ArrayList; 72import java.util.Arrays; 73import java.util.Collections; 74import java.util.List; 75import java.util.concurrent.CopyOnWriteArrayList; 76 77/** 78 * Static library support version of the framework's {@link android.app.FragmentManager}. 79 * Used to write apps that run on platforms prior to Android 3.0. When running 80 * on Android 3.0 or above, this implementation is still used; it does not try 81 * to switch to the framework's implementation. See the framework {@link FragmentManager} 82 * documentation for a class overview. 83 * 84 * <p>Your activity must derive from {@link FragmentActivity} to use this. From such an activity, 85 * you can acquire the {@link FragmentManager} by calling 86 * {@link FragmentActivity#getSupportFragmentManager}. 87 */ 88public abstract class FragmentManager { 89 /** 90 * Representation of an entry on the fragment back stack, as created 91 * with {@link FragmentTransaction#addToBackStack(String) 92 * FragmentTransaction.addToBackStack()}. Entries can later be 93 * retrieved with {@link FragmentManager#getBackStackEntryAt(int) 94 * FragmentManager.getBackStackEntryAt()}. 95 * 96 * <p>Note that you should never hold on to a BackStackEntry object; 97 * the identifier as returned by {@link #getId} is the only thing that 98 * will be persisted across activity instances. 99 */ 100 public interface BackStackEntry { 101 /** 102 * Return the unique identifier for the entry. This is the only 103 * representation of the entry that will persist across activity 104 * instances. 105 */ 106 public int getId(); 107 108 /** 109 * Get the name that was supplied to 110 * {@link FragmentTransaction#addToBackStack(String) 111 * FragmentTransaction.addToBackStack(String)} when creating this entry. 112 */ 113 @Nullable 114 public String getName(); 115 116 /** 117 * Return the full bread crumb title resource identifier for the entry, 118 * or 0 if it does not have one. 119 */ 120 @StringRes 121 public int getBreadCrumbTitleRes(); 122 123 /** 124 * Return the short bread crumb title resource identifier for the entry, 125 * or 0 if it does not have one. 126 */ 127 @StringRes 128 public int getBreadCrumbShortTitleRes(); 129 130 /** 131 * Return the full bread crumb title for the entry, or null if it 132 * does not have one. 133 */ 134 @Nullable 135 public CharSequence getBreadCrumbTitle(); 136 137 /** 138 * Return the short bread crumb title for the entry, or null if it 139 * does not have one. 140 */ 141 @Nullable 142 public CharSequence getBreadCrumbShortTitle(); 143 } 144 145 /** 146 * Interface to watch for changes to the back stack. 147 */ 148 public interface OnBackStackChangedListener { 149 /** 150 * Called whenever the contents of the back stack change. 151 */ 152 public void onBackStackChanged(); 153 } 154 155 /** 156 * Start a series of edit operations on the Fragments associated with 157 * this FragmentManager. 158 * 159 * <p>Note: A fragment transaction can only be created/committed prior 160 * to an activity saving its state. If you try to commit a transaction 161 * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()} 162 * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart} 163 * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error. 164 * This is because the framework takes care of saving your current fragments 165 * in the state, and if changes are made after the state is saved then they 166 * will be lost.</p> 167 */ 168 @NonNull 169 public abstract FragmentTransaction beginTransaction(); 170 171 /** 172 * @hide -- remove once prebuilts are in. 173 * @deprecated 174 */ 175 @RestrictTo(LIBRARY_GROUP) 176 @Deprecated 177 public FragmentTransaction openTransaction() { 178 return beginTransaction(); 179 } 180 181 /** 182 * After a {@link FragmentTransaction} is committed with 183 * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it 184 * is scheduled to be executed asynchronously on the process's main thread. 185 * If you want to immediately executing any such pending operations, you 186 * can call this function (only from the main thread) to do so. Note that 187 * all callbacks and other related behavior will be done from within this 188 * call, so be careful about where this is called from. 189 * 190 * <p>If you are committing a single transaction that does not modify the 191 * fragment back stack, strongly consider using 192 * {@link FragmentTransaction#commitNow()} instead. This can help avoid 193 * unwanted side effects when other code in your app has pending committed 194 * transactions that expect different timing.</p> 195 * <p> 196 * This also forces the start of any postponed Transactions where 197 * {@link Fragment#postponeEnterTransition()} has been called. 198 * 199 * @return Returns true if there were any pending transactions to be 200 * executed. 201 */ 202 public abstract boolean executePendingTransactions(); 203 204 /** 205 * Finds a fragment that was identified by the given id either when inflated 206 * from XML or as the container ID when added in a transaction. This first 207 * searches through fragments that are currently added to the manager's 208 * activity; if no such fragment is found, then all fragments currently 209 * on the back stack associated with this ID are searched. 210 * @return The fragment if found or null otherwise. 211 */ 212 @Nullable 213 public abstract Fragment findFragmentById(@IdRes int id); 214 215 /** 216 * Finds a fragment that was identified by the given tag either when inflated 217 * from XML or as supplied when added in a transaction. This first 218 * searches through fragments that are currently added to the manager's 219 * activity; if no such fragment is found, then all fragments currently 220 * on the back stack are searched. 221 * @return The fragment if found or null otherwise. 222 */ 223 @Nullable 224 public abstract Fragment findFragmentByTag(@Nullable String tag); 225 226 /** 227 * Flag for {@link #popBackStack(String, int)} 228 * and {@link #popBackStack(int, int)}: If set, and the name or ID of 229 * a back stack entry has been supplied, then all matching entries will 230 * be consumed until one that doesn't match is found or the bottom of 231 * the stack is reached. Otherwise, all entries up to but not including that entry 232 * will be removed. 233 */ 234 public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; 235 236 /** 237 * Pop the top state off the back stack. Returns true if there was one 238 * to pop, else false. This function is asynchronous -- it enqueues the 239 * request to pop, but the action will not be performed until the application 240 * returns to its event loop. 241 */ 242 public abstract void popBackStack(); 243 244 /** 245 * Like {@link #popBackStack()}, but performs the operation immediately 246 * inside of the call. This is like calling {@link #executePendingTransactions()} 247 * afterwards without forcing the start of postponed Transactions. 248 * @return Returns true if there was something popped, else false. 249 */ 250 public abstract boolean popBackStackImmediate(); 251 252 /** 253 * Pop the last fragment transition from the manager's fragment 254 * back stack. If there is nothing to pop, false is returned. 255 * This function is asynchronous -- it enqueues the 256 * request to pop, but the action will not be performed until the application 257 * returns to its event loop. 258 * 259 * @param name If non-null, this is the name of a previous back state 260 * to look for; if found, all states up to that state will be popped. The 261 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 262 * the named state itself is popped. If null, only the top state is popped. 263 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 264 */ 265 public abstract void popBackStack(@Nullable String name, int flags); 266 267 /** 268 * Like {@link #popBackStack(String, int)}, but performs the operation immediately 269 * inside of the call. This is like calling {@link #executePendingTransactions()} 270 * afterwards without forcing the start of postponed Transactions. 271 * @return Returns true if there was something popped, else false. 272 */ 273 public abstract boolean popBackStackImmediate(@Nullable String name, int flags); 274 275 /** 276 * Pop all back stack states up to the one with the given identifier. 277 * This function is asynchronous -- it enqueues the 278 * request to pop, but the action will not be performed until the application 279 * returns to its event loop. 280 * 281 * @param id Identifier of the stated to be popped. If no identifier exists, 282 * false is returned. 283 * The identifier is the number returned by 284 * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The 285 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 286 * the named state itself is popped. 287 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 288 */ 289 public abstract void popBackStack(int id, int flags); 290 291 /** 292 * Like {@link #popBackStack(int, int)}, but performs the operation immediately 293 * inside of the call. This is like calling {@link #executePendingTransactions()} 294 * afterwards without forcing the start of postponed Transactions. 295 * @return Returns true if there was something popped, else false. 296 */ 297 public abstract boolean popBackStackImmediate(int id, int flags); 298 299 /** 300 * Return the number of entries currently in the back stack. 301 */ 302 public abstract int getBackStackEntryCount(); 303 304 /** 305 * Return the BackStackEntry at index <var>index</var> in the back stack; 306 * entries start index 0 being the bottom of the stack. 307 */ 308 @NonNull 309 public abstract BackStackEntry getBackStackEntryAt(int index); 310 311 /** 312 * Add a new listener for changes to the fragment back stack. 313 */ 314 public abstract void addOnBackStackChangedListener( 315 @NonNull OnBackStackChangedListener listener); 316 317 /** 318 * Remove a listener that was previously added with 319 * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}. 320 */ 321 public abstract void removeOnBackStackChangedListener( 322 @NonNull OnBackStackChangedListener listener); 323 324 /** 325 * Put a reference to a fragment in a Bundle. This Bundle can be 326 * persisted as saved state, and when later restoring 327 * {@link #getFragment(Bundle, String)} will return the current 328 * instance of the same fragment. 329 * 330 * @param bundle The bundle in which to put the fragment reference. 331 * @param key The name of the entry in the bundle. 332 * @param fragment The Fragment whose reference is to be stored. 333 */ 334 public abstract void putFragment(@NonNull Bundle bundle, @NonNull String key, 335 @NonNull Fragment fragment); 336 337 /** 338 * Retrieve the current Fragment instance for a reference previously 339 * placed with {@link #putFragment(Bundle, String, Fragment)}. 340 * 341 * @param bundle The bundle from which to retrieve the fragment reference. 342 * @param key The name of the entry in the bundle. 343 * @return Returns the current Fragment instance that is associated with 344 * the given reference. 345 */ 346 @Nullable 347 public abstract Fragment getFragment(@NonNull Bundle bundle, @NonNull String key); 348 349 /** 350 * Get a list of all fragments that are currently added to the FragmentManager. 351 * This may include those that are hidden as well as those that are shown. 352 * This will not include any fragments only in the back stack, or fragments that 353 * are detached or removed. 354 * <p> 355 * The order of the fragments in the list is the order in which they were 356 * added or attached. 357 * 358 * @return A list of all fragments that are added to the FragmentManager. 359 */ 360 @NonNull 361 public abstract List<Fragment> getFragments(); 362 363 /** 364 * Save the current instance state of the given Fragment. This can be 365 * used later when creating a new instance of the Fragment and adding 366 * it to the fragment manager, to have it create itself to match the 367 * current state returned here. Note that there are limits on how 368 * this can be used: 369 * 370 * <ul> 371 * <li>The Fragment must currently be attached to the FragmentManager. 372 * <li>A new Fragment created using this saved state must be the same class 373 * type as the Fragment it was created from. 374 * <li>The saved state can not contain dependencies on other fragments -- 375 * that is it can't use {@link #putFragment(Bundle, String, Fragment)} to 376 * store a fragment reference because that reference may not be valid when 377 * this saved state is later used. Likewise the Fragment's target and 378 * result code are not included in this state. 379 * </ul> 380 * 381 * @param f The Fragment whose state is to be saved. 382 * @return The generated state. This will be null if there was no 383 * interesting state created by the fragment. 384 */ 385 @Nullable 386 public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f); 387 388 /** 389 * Returns true if the final {@link android.app.Activity#onDestroy() Activity.onDestroy()} 390 * call has been made on the FragmentManager's Activity, so this instance is now dead. 391 */ 392 public abstract boolean isDestroyed(); 393 394 /** 395 * Registers a {@link FragmentLifecycleCallbacks} to listen to fragment lifecycle events 396 * happening in this FragmentManager. All registered callbacks will be automatically 397 * unregistered when this FragmentManager is destroyed. 398 * 399 * @param cb Callbacks to register 400 * @param recursive true to automatically register this callback for all child FragmentManagers 401 */ 402 public abstract void registerFragmentLifecycleCallbacks(@NonNull FragmentLifecycleCallbacks cb, 403 boolean recursive); 404 405 /** 406 * Unregisters a previously registered {@link FragmentLifecycleCallbacks}. If the callback 407 * was not previously registered this call has no effect. All registered callbacks will be 408 * automatically unregistered when this FragmentManager is destroyed. 409 * 410 * @param cb Callbacks to unregister 411 */ 412 public abstract void unregisterFragmentLifecycleCallbacks( 413 @NonNull FragmentLifecycleCallbacks cb); 414 415 /** 416 * Return the currently active primary navigation fragment for this FragmentManager. 417 * The primary navigation fragment is set by fragment transactions using 418 * {@link FragmentTransaction#setPrimaryNavigationFragment(Fragment)}. 419 * 420 * <p>The primary navigation fragment's 421 * {@link Fragment#getChildFragmentManager() child FragmentManager} will be called first 422 * to process delegated navigation actions such as {@link #popBackStack()} if no ID 423 * or transaction name is provided to pop to.</p> 424 * 425 * @return the fragment designated as the primary navigation fragment 426 */ 427 @Nullable 428 public abstract Fragment getPrimaryNavigationFragment(); 429 430 /** 431 * Print the FragmentManager's state into the given stream. 432 * 433 * @param prefix Text to print at the front of each line. 434 * @param fd The raw file descriptor that the dump is being sent to. 435 * @param writer A PrintWriter to which the dump is to be set. 436 * @param args Additional arguments to the dump request. 437 */ 438 public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); 439 440 /** 441 * Control whether the framework's internal fragment manager debugging 442 * logs are turned on. If enabled, you will see output in logcat as 443 * the framework performs fragment operations. 444 */ 445 public static void enableDebugLogging(boolean enabled) { 446 FragmentManagerImpl.DEBUG = enabled; 447 } 448 449 /** 450 * Returns {@code true} if the FragmentManager's state has already been saved 451 * by its host. Any operations that would change saved state should not be performed 452 * if this method returns true. For example, any popBackStack() method, such as 453 * {@link #popBackStackImmediate()} or any FragmentTransaction using 454 * {@link FragmentTransaction#commit()} instead of 455 * {@link FragmentTransaction#commitAllowingStateLoss()} will change 456 * the state and will result in an error. 457 * 458 * @return true if this FragmentManager's state has already been saved by its host 459 */ 460 public abstract boolean isStateSaved(); 461 462 /** 463 * Callback interface for listening to fragment state changes that happen 464 * within a given FragmentManager. 465 */ 466 public abstract static class FragmentLifecycleCallbacks { 467 /** 468 * Called right before the fragment's {@link Fragment#onAttach(Context)} method is called. 469 * This is a good time to inject any required dependencies or perform other configuration 470 * for the fragment before any of the fragment's lifecycle methods are invoked. 471 * 472 * @param fm Host FragmentManager 473 * @param f Fragment changing state 474 * @param context Context that the Fragment is being attached to 475 */ 476 public void onFragmentPreAttached(@NonNull FragmentManager fm, @NonNull Fragment f, 477 @NonNull Context context) {} 478 479 /** 480 * Called after the fragment has been attached to its host. Its host will have had 481 * <code>onAttachFragment</code> called before this call happens. 482 * 483 * @param fm Host FragmentManager 484 * @param f Fragment changing state 485 * @param context Context that the Fragment was attached to 486 */ 487 public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, 488 @NonNull Context context) {} 489 490 /** 491 * Called right before the fragment's {@link Fragment#onCreate(Bundle)} method is called. 492 * This is a good time to inject any required dependencies or perform other configuration 493 * for the fragment. 494 * 495 * @param fm Host FragmentManager 496 * @param f Fragment changing state 497 * @param savedInstanceState Saved instance bundle from a previous instance 498 */ 499 public void onFragmentPreCreated(@NonNull FragmentManager fm, @NonNull Fragment f, 500 @Nullable Bundle savedInstanceState) {} 501 502 /** 503 * Called after the fragment has returned from the FragmentManager's call to 504 * {@link Fragment#onCreate(Bundle)}. This will only happen once for any given 505 * fragment instance, though the fragment may be attached and detached multiple times. 506 * 507 * @param fm Host FragmentManager 508 * @param f Fragment changing state 509 * @param savedInstanceState Saved instance bundle from a previous instance 510 */ 511 public void onFragmentCreated(@NonNull FragmentManager fm, @NonNull Fragment f, 512 @Nullable Bundle savedInstanceState) {} 513 514 /** 515 * Called after the fragment has returned from the FragmentManager's call to 516 * {@link Fragment#onActivityCreated(Bundle)}. This will only happen once for any given 517 * fragment instance, though the fragment may be attached and detached multiple times. 518 * 519 * @param fm Host FragmentManager 520 * @param f Fragment changing state 521 * @param savedInstanceState Saved instance bundle from a previous instance 522 */ 523 public void onFragmentActivityCreated(@NonNull FragmentManager fm, @NonNull Fragment f, 524 @Nullable Bundle savedInstanceState) {} 525 526 /** 527 * Called after the fragment has returned a non-null view from the FragmentManager's 528 * request to {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}. 529 * 530 * @param fm Host FragmentManager 531 * @param f Fragment that created and owns the view 532 * @param v View returned by the fragment 533 * @param savedInstanceState Saved instance bundle from a previous instance 534 */ 535 public void onFragmentViewCreated(@NonNull FragmentManager fm, @NonNull Fragment f, 536 @NonNull View v, @Nullable Bundle savedInstanceState) {} 537 538 /** 539 * Called after the fragment has returned from the FragmentManager's call to 540 * {@link Fragment#onStart()}. 541 * 542 * @param fm Host FragmentManager 543 * @param f Fragment changing state 544 */ 545 public void onFragmentStarted(@NonNull FragmentManager fm, @NonNull Fragment f) {} 546 547 /** 548 * Called after the fragment has returned from the FragmentManager's call to 549 * {@link Fragment#onResume()}. 550 * 551 * @param fm Host FragmentManager 552 * @param f Fragment changing state 553 */ 554 public void onFragmentResumed(@NonNull FragmentManager fm, @NonNull Fragment f) {} 555 556 /** 557 * Called after the fragment has returned from the FragmentManager's call to 558 * {@link Fragment#onPause()}. 559 * 560 * @param fm Host FragmentManager 561 * @param f Fragment changing state 562 */ 563 public void onFragmentPaused(@NonNull FragmentManager fm, @NonNull Fragment f) {} 564 565 /** 566 * Called after the fragment has returned from the FragmentManager's call to 567 * {@link Fragment#onStop()}. 568 * 569 * @param fm Host FragmentManager 570 * @param f Fragment changing state 571 */ 572 public void onFragmentStopped(@NonNull FragmentManager fm, @NonNull Fragment f) {} 573 574 /** 575 * Called after the fragment has returned from the FragmentManager's call to 576 * {@link Fragment#onSaveInstanceState(Bundle)}. 577 * 578 * @param fm Host FragmentManager 579 * @param f Fragment changing state 580 * @param outState Saved state bundle for the fragment 581 */ 582 public void onFragmentSaveInstanceState(@NonNull FragmentManager fm, @NonNull Fragment f, 583 @NonNull Bundle outState) {} 584 585 /** 586 * Called after the fragment has returned from the FragmentManager's call to 587 * {@link Fragment#onDestroyView()}. 588 * 589 * @param fm Host FragmentManager 590 * @param f Fragment changing state 591 */ 592 public void onFragmentViewDestroyed(@NonNull FragmentManager fm, @NonNull Fragment f) {} 593 594 /** 595 * Called after the fragment has returned from the FragmentManager's call to 596 * {@link Fragment#onDestroy()}. 597 * 598 * @param fm Host FragmentManager 599 * @param f Fragment changing state 600 */ 601 public void onFragmentDestroyed(@NonNull FragmentManager fm, @NonNull Fragment f) {} 602 603 /** 604 * Called after the fragment has returned from the FragmentManager's call to 605 * {@link Fragment#onDetach()}. 606 * 607 * @param fm Host FragmentManager 608 * @param f Fragment changing state 609 */ 610 public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f) {} 611 } 612} 613 614final class FragmentManagerState implements Parcelable { 615 FragmentState[] mActive; 616 int[] mAdded; 617 BackStackState[] mBackStack; 618 int mPrimaryNavActiveIndex = -1; 619 int mNextFragmentIndex; 620 621 public FragmentManagerState() { 622 } 623 624 public FragmentManagerState(Parcel in) { 625 mActive = in.createTypedArray(FragmentState.CREATOR); 626 mAdded = in.createIntArray(); 627 mBackStack = in.createTypedArray(BackStackState.CREATOR); 628 mPrimaryNavActiveIndex = in.readInt(); 629 mNextFragmentIndex = in.readInt(); 630 } 631 632 @Override 633 public int describeContents() { 634 return 0; 635 } 636 637 @Override 638 public void writeToParcel(Parcel dest, int flags) { 639 dest.writeTypedArray(mActive, flags); 640 dest.writeIntArray(mAdded); 641 dest.writeTypedArray(mBackStack, flags); 642 dest.writeInt(mPrimaryNavActiveIndex); 643 dest.writeInt(mNextFragmentIndex); 644 } 645 646 public static final Parcelable.Creator<FragmentManagerState> CREATOR 647 = new Parcelable.Creator<FragmentManagerState>() { 648 @Override 649 public FragmentManagerState createFromParcel(Parcel in) { 650 return new FragmentManagerState(in); 651 } 652 653 @Override 654 public FragmentManagerState[] newArray(int size) { 655 return new FragmentManagerState[size]; 656 } 657 }; 658} 659 660/** 661 * Container for fragments associated with an activity. 662 */ 663final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 { 664 static boolean DEBUG = false; 665 static final String TAG = "FragmentManager"; 666 667 static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state"; 668 static final String TARGET_STATE_TAG = "android:target_state"; 669 static final String VIEW_STATE_TAG = "android:view_state"; 670 static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint"; 671 672 private static final class FragmentLifecycleCallbacksHolder { 673 final FragmentLifecycleCallbacks mCallback; 674 final boolean mRecursive; 675 676 FragmentLifecycleCallbacksHolder(FragmentLifecycleCallbacks callback, boolean recursive) { 677 mCallback = callback; 678 mRecursive = recursive; 679 } 680 } 681 682 ArrayList<OpGenerator> mPendingActions; 683 boolean mExecutingActions; 684 685 int mNextFragmentIndex = 0; 686 687 final ArrayList<Fragment> mAdded = new ArrayList<>(); 688 SparseArray<Fragment> mActive; 689 ArrayList<BackStackRecord> mBackStack; 690 ArrayList<Fragment> mCreatedMenus; 691 692 // Must be accessed while locked. 693 ArrayList<BackStackRecord> mBackStackIndices; 694 ArrayList<Integer> mAvailBackStackIndices; 695 696 ArrayList<OnBackStackChangedListener> mBackStackChangeListeners; 697 private final CopyOnWriteArrayList<FragmentLifecycleCallbacksHolder> 698 mLifecycleCallbacks = new CopyOnWriteArrayList<>(); 699 700 int mCurState = Fragment.INITIALIZING; 701 FragmentHostCallback mHost; 702 FragmentContainer mContainer; 703 Fragment mParent; 704 @Nullable Fragment mPrimaryNav; 705 706 static Field sAnimationListenerField = null; 707 708 boolean mNeedMenuInvalidate; 709 boolean mStateSaved; 710 boolean mStopped; 711 boolean mDestroyed; 712 String mNoTransactionsBecause; 713 boolean mHavePendingDeferredStart; 714 715 // Temporary vars for removing redundant operations in BackStackRecords: 716 ArrayList<BackStackRecord> mTmpRecords; 717 ArrayList<Boolean> mTmpIsPop; 718 ArrayList<Fragment> mTmpAddedFragments; 719 720 // Temporary vars for state save and restore. 721 Bundle mStateBundle = null; 722 SparseArray<Parcelable> mStateArray = null; 723 724 // Postponed transactions. 725 ArrayList<StartEnterTransitionListener> mPostponedTransactions; 726 727 // Saved FragmentManagerNonConfig during saveAllState() and cleared in noteStateNotSaved() 728 FragmentManagerNonConfig mSavedNonConfig; 729 730 Runnable mExecCommit = new Runnable() { 731 @Override 732 public void run() { 733 execPendingActions(); 734 } 735 }; 736 737 static boolean modifiesAlpha(AnimationOrAnimator anim) { 738 if (anim.animation instanceof AlphaAnimation) { 739 return true; 740 } else if (anim.animation instanceof AnimationSet) { 741 List<Animation> anims = ((AnimationSet) anim.animation).getAnimations(); 742 for (int i = 0; i < anims.size(); i++) { 743 if (anims.get(i) instanceof AlphaAnimation) { 744 return true; 745 } 746 } 747 return false; 748 } else { 749 return modifiesAlpha(anim.animator); 750 } 751 } 752 753 static boolean modifiesAlpha(Animator anim) { 754 if (anim == null) { 755 return false; 756 } 757 if (anim instanceof ValueAnimator) { 758 ValueAnimator valueAnim = (ValueAnimator) anim; 759 PropertyValuesHolder[] values = valueAnim.getValues(); 760 for (int i = 0; i < values.length; i++) { 761 if (("alpha").equals(values[i].getPropertyName())) { 762 return true; 763 } 764 } 765 } else if (anim instanceof AnimatorSet) { 766 List<Animator> animList = ((AnimatorSet) anim).getChildAnimations(); 767 for (int i = 0; i < animList.size(); i++) { 768 if (modifiesAlpha(animList.get(i))) { 769 return true; 770 } 771 } 772 } 773 return false; 774 } 775 776 static boolean shouldRunOnHWLayer(View v, AnimationOrAnimator anim) { 777 if (v == null || anim == null) { 778 return false; 779 } 780 return Build.VERSION.SDK_INT >= 19 781 && v.getLayerType() == View.LAYER_TYPE_NONE 782 && ViewCompat.hasOverlappingRendering(v) 783 && modifiesAlpha(anim); 784 } 785 786 private void throwException(RuntimeException ex) { 787 Log.e(TAG, ex.getMessage()); 788 Log.e(TAG, "Activity state:"); 789 LogWriter logw = new LogWriter(TAG); 790 PrintWriter pw = new PrintWriter(logw); 791 if (mHost != null) { 792 try { 793 mHost.onDump(" ", null, pw, new String[] { }); 794 } catch (Exception e) { 795 Log.e(TAG, "Failed dumping state", e); 796 } 797 } else { 798 try { 799 dump(" ", null, pw, new String[] { }); 800 } catch (Exception e) { 801 Log.e(TAG, "Failed dumping state", e); 802 } 803 } 804 throw ex; 805 } 806 807 @Override 808 public FragmentTransaction beginTransaction() { 809 return new BackStackRecord(this); 810 } 811 812 @Override 813 public boolean executePendingTransactions() { 814 boolean updates = execPendingActions(); 815 forcePostponedTransactions(); 816 return updates; 817 } 818 819 @Override 820 public void popBackStack() { 821 enqueueAction(new PopBackStackState(null, -1, 0), false); 822 } 823 824 @Override 825 public boolean popBackStackImmediate() { 826 checkStateLoss(); 827 return popBackStackImmediate(null, -1, 0); 828 } 829 830 @Override 831 public void popBackStack(@Nullable final String name, final int flags) { 832 enqueueAction(new PopBackStackState(name, -1, flags), false); 833 } 834 835 @Override 836 public boolean popBackStackImmediate(@Nullable String name, int flags) { 837 checkStateLoss(); 838 return popBackStackImmediate(name, -1, flags); 839 } 840 841 @Override 842 public void popBackStack(final int id, final int flags) { 843 if (id < 0) { 844 throw new IllegalArgumentException("Bad id: " + id); 845 } 846 enqueueAction(new PopBackStackState(null, id, flags), false); 847 } 848 849 @Override 850 public boolean popBackStackImmediate(int id, int flags) { 851 checkStateLoss(); 852 execPendingActions(); 853 if (id < 0) { 854 throw new IllegalArgumentException("Bad id: " + id); 855 } 856 return popBackStackImmediate(null, id, flags); 857 } 858 859 /** 860 * Used by all public popBackStackImmediate methods, this executes pending transactions and 861 * returns true if the pop action did anything, regardless of what other pending 862 * transactions did. 863 * 864 * @return true if the pop operation did anything or false otherwise. 865 */ 866 private boolean popBackStackImmediate(String name, int id, int flags) { 867 execPendingActions(); 868 ensureExecReady(true); 869 870 if (mPrimaryNav != null // We have a primary nav fragment 871 && id < 0 // No valid id (since they're local) 872 && name == null) { // no name to pop to (since they're local) 873 final FragmentManager childManager = mPrimaryNav.peekChildFragmentManager(); 874 if (childManager != null && childManager.popBackStackImmediate()) { 875 // We did something, just not to this specific FragmentManager. Return true. 876 return true; 877 } 878 } 879 880 boolean executePop = popBackStackState(mTmpRecords, mTmpIsPop, name, id, flags); 881 if (executePop) { 882 mExecutingActions = true; 883 try { 884 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop); 885 } finally { 886 cleanupExec(); 887 } 888 } 889 890 doPendingDeferredStart(); 891 burpActive(); 892 return executePop; 893 } 894 895 @Override 896 public int getBackStackEntryCount() { 897 return mBackStack != null ? mBackStack.size() : 0; 898 } 899 900 @Override 901 public BackStackEntry getBackStackEntryAt(int index) { 902 return mBackStack.get(index); 903 } 904 905 @Override 906 public void addOnBackStackChangedListener(OnBackStackChangedListener listener) { 907 if (mBackStackChangeListeners == null) { 908 mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>(); 909 } 910 mBackStackChangeListeners.add(listener); 911 } 912 913 @Override 914 public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) { 915 if (mBackStackChangeListeners != null) { 916 mBackStackChangeListeners.remove(listener); 917 } 918 } 919 920 @Override 921 public void putFragment(Bundle bundle, String key, Fragment fragment) { 922 if (fragment.mIndex < 0) { 923 throwException(new IllegalStateException("Fragment " + fragment 924 + " is not currently in the FragmentManager")); 925 } 926 bundle.putInt(key, fragment.mIndex); 927 } 928 929 @Override 930 @Nullable 931 public Fragment getFragment(Bundle bundle, String key) { 932 int index = bundle.getInt(key, -1); 933 if (index == -1) { 934 return null; 935 } 936 Fragment f = mActive.get(index); 937 if (f == null) { 938 throwException(new IllegalStateException("Fragment no longer exists for key " 939 + key + ": index " + index)); 940 } 941 return f; 942 } 943 944 @Override 945 public List<Fragment> getFragments() { 946 if (mAdded.isEmpty()) { 947 return Collections.EMPTY_LIST; 948 } 949 synchronized (mAdded) { 950 return (List<Fragment>) mAdded.clone(); 951 } 952 } 953 954 /** 955 * This is used by FragmentController to get the Active fragments. 956 * 957 * @return A list of active fragments in the fragment manager, including those that are in the 958 * back stack. 959 */ 960 List<Fragment> getActiveFragments() { 961 if (mActive == null) { 962 return null; 963 } 964 final int count = mActive.size(); 965 ArrayList<Fragment> fragments = new ArrayList<>(count); 966 for (int i = 0; i < count; i++) { 967 fragments.add(mActive.valueAt(i)); 968 } 969 return fragments; 970 } 971 972 /** 973 * Used by FragmentController to get the number of Active Fragments. 974 * 975 * @return The number of active fragments. 976 */ 977 int getActiveFragmentCount() { 978 if (mActive == null) { 979 return 0; 980 } 981 return mActive.size(); 982 } 983 984 @Override 985 @Nullable 986 public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) { 987 if (fragment.mIndex < 0) { 988 throwException( new IllegalStateException("Fragment " + fragment 989 + " is not currently in the FragmentManager")); 990 } 991 if (fragment.mState > Fragment.INITIALIZING) { 992 Bundle result = saveFragmentBasicState(fragment); 993 return result != null ? new Fragment.SavedState(result) : null; 994 } 995 return null; 996 } 997 998 @Override 999 public boolean isDestroyed() { 1000 return mDestroyed; 1001 } 1002 1003 @Override 1004 public String toString() { 1005 StringBuilder sb = new StringBuilder(128); 1006 sb.append("FragmentManager{"); 1007 sb.append(Integer.toHexString(System.identityHashCode(this))); 1008 sb.append(" in "); 1009 if (mParent != null) { 1010 DebugUtils.buildShortClassTag(mParent, sb); 1011 } else { 1012 DebugUtils.buildShortClassTag(mHost, sb); 1013 } 1014 sb.append("}}"); 1015 return sb.toString(); 1016 } 1017 1018 @Override 1019 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 1020 String innerPrefix = prefix + " "; 1021 1022 int N; 1023 if (mActive != null) { 1024 N = mActive.size(); 1025 if (N > 0) { 1026 writer.print(prefix); writer.print("Active Fragments in "); 1027 writer.print(Integer.toHexString(System.identityHashCode(this))); 1028 writer.println(":"); 1029 for (int i=0; i<N; i++) { 1030 Fragment f = mActive.valueAt(i); 1031 writer.print(prefix); writer.print(" #"); writer.print(i); 1032 writer.print(": "); writer.println(f); 1033 if (f != null) { 1034 f.dump(innerPrefix, fd, writer, args); 1035 } 1036 } 1037 } 1038 } 1039 1040 N = mAdded.size(); 1041 if (N > 0) { 1042 writer.print(prefix); writer.println("Added Fragments:"); 1043 for (int i = 0; i < N; i++) { 1044 Fragment f = mAdded.get(i); 1045 writer.print(prefix); 1046 writer.print(" #"); 1047 writer.print(i); 1048 writer.print(": "); 1049 writer.println(f.toString()); 1050 } 1051 } 1052 1053 if (mCreatedMenus != null) { 1054 N = mCreatedMenus.size(); 1055 if (N > 0) { 1056 writer.print(prefix); writer.println("Fragments Created Menus:"); 1057 for (int i=0; i<N; i++) { 1058 Fragment f = mCreatedMenus.get(i); 1059 writer.print(prefix); writer.print(" #"); writer.print(i); 1060 writer.print(": "); writer.println(f.toString()); 1061 } 1062 } 1063 } 1064 1065 if (mBackStack != null) { 1066 N = mBackStack.size(); 1067 if (N > 0) { 1068 writer.print(prefix); writer.println("Back Stack:"); 1069 for (int i=0; i<N; i++) { 1070 BackStackRecord bs = mBackStack.get(i); 1071 writer.print(prefix); writer.print(" #"); writer.print(i); 1072 writer.print(": "); writer.println(bs.toString()); 1073 bs.dump(innerPrefix, fd, writer, args); 1074 } 1075 } 1076 } 1077 1078 synchronized (this) { 1079 if (mBackStackIndices != null) { 1080 N = mBackStackIndices.size(); 1081 if (N > 0) { 1082 writer.print(prefix); writer.println("Back Stack Indices:"); 1083 for (int i=0; i<N; i++) { 1084 BackStackRecord bs = mBackStackIndices.get(i); 1085 writer.print(prefix); writer.print(" #"); writer.print(i); 1086 writer.print(": "); writer.println(bs); 1087 } 1088 } 1089 } 1090 1091 if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) { 1092 writer.print(prefix); writer.print("mAvailBackStackIndices: "); 1093 writer.println(Arrays.toString(mAvailBackStackIndices.toArray())); 1094 } 1095 } 1096 1097 if (mPendingActions != null) { 1098 N = mPendingActions.size(); 1099 if (N > 0) { 1100 writer.print(prefix); writer.println("Pending Actions:"); 1101 for (int i=0; i<N; i++) { 1102 OpGenerator r = mPendingActions.get(i); 1103 writer.print(prefix); writer.print(" #"); writer.print(i); 1104 writer.print(": "); writer.println(r); 1105 } 1106 } 1107 } 1108 1109 writer.print(prefix); writer.println("FragmentManager misc state:"); 1110 writer.print(prefix); writer.print(" mHost="); writer.println(mHost); 1111 writer.print(prefix); writer.print(" mContainer="); writer.println(mContainer); 1112 if (mParent != null) { 1113 writer.print(prefix); writer.print(" mParent="); writer.println(mParent); 1114 } 1115 writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState); 1116 writer.print(" mStateSaved="); writer.print(mStateSaved); 1117 writer.print(" mStopped="); writer.print(mStopped); 1118 writer.print(" mDestroyed="); writer.println(mDestroyed); 1119 if (mNeedMenuInvalidate) { 1120 writer.print(prefix); writer.print(" mNeedMenuInvalidate="); 1121 writer.println(mNeedMenuInvalidate); 1122 } 1123 if (mNoTransactionsBecause != null) { 1124 writer.print(prefix); writer.print(" mNoTransactionsBecause="); 1125 writer.println(mNoTransactionsBecause); 1126 } 1127 } 1128 1129 static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f); 1130 static final Interpolator DECELERATE_CUBIC = new DecelerateInterpolator(1.5f); 1131 static final Interpolator ACCELERATE_QUINT = new AccelerateInterpolator(2.5f); 1132 static final Interpolator ACCELERATE_CUBIC = new AccelerateInterpolator(1.5f); 1133 1134 static final int ANIM_DUR = 220; 1135 1136 static AnimationOrAnimator makeOpenCloseAnimation(Context context, float startScale, 1137 float endScale, float startAlpha, float endAlpha) { 1138 AnimationSet set = new AnimationSet(false); 1139 ScaleAnimation scale = new ScaleAnimation(startScale, endScale, startScale, endScale, 1140 Animation.RELATIVE_TO_SELF, .5f, Animation.RELATIVE_TO_SELF, .5f); 1141 scale.setInterpolator(DECELERATE_QUINT); 1142 scale.setDuration(ANIM_DUR); 1143 set.addAnimation(scale); 1144 AlphaAnimation alpha = new AlphaAnimation(startAlpha, endAlpha); 1145 alpha.setInterpolator(DECELERATE_CUBIC); 1146 alpha.setDuration(ANIM_DUR); 1147 set.addAnimation(alpha); 1148 return new AnimationOrAnimator(set); 1149 } 1150 1151 static AnimationOrAnimator makeFadeAnimation(Context context, float start, float end) { 1152 AlphaAnimation anim = new AlphaAnimation(start, end); 1153 anim.setInterpolator(DECELERATE_CUBIC); 1154 anim.setDuration(ANIM_DUR); 1155 return new AnimationOrAnimator(anim); 1156 } 1157 1158 AnimationOrAnimator loadAnimation(Fragment fragment, int transit, boolean enter, 1159 int transitionStyle) { 1160 int nextAnim = fragment.getNextAnim(); 1161 Animation animation = fragment.onCreateAnimation(transit, enter, nextAnim); 1162 if (animation != null) { 1163 return new AnimationOrAnimator(animation); 1164 } 1165 1166 Animator animator = fragment.onCreateAnimator(transit, enter, nextAnim); 1167 if (animator != null) { 1168 return new AnimationOrAnimator(animator); 1169 } 1170 1171 if (nextAnim != 0) { 1172 String dir = mHost.getContext().getResources().getResourceTypeName(nextAnim); 1173 boolean isAnim = "anim".equals(dir); 1174 boolean successfulLoad = false; 1175 if (isAnim) { 1176 // try AnimationUtils first 1177 try { 1178 animation = AnimationUtils.loadAnimation(mHost.getContext(), nextAnim); 1179 if (animation != null) { 1180 return new AnimationOrAnimator(animation); 1181 } 1182 // A null animation may be returned and that is acceptable 1183 successfulLoad = true; // succeeded in loading animation, but it is null 1184 } catch (NotFoundException e) { 1185 throw e; // Rethrow it -- the resource should be found if it is provided. 1186 } catch (RuntimeException e) { 1187 // Other exceptions can occur when loading an Animator from AnimationUtils. 1188 } 1189 } 1190 if (!successfulLoad) { 1191 // try Animator 1192 try { 1193 animator = AnimatorInflater.loadAnimator(mHost.getContext(), nextAnim); 1194 if (animator != null) { 1195 return new AnimationOrAnimator(animator); 1196 } 1197 } catch (RuntimeException e) { 1198 if (isAnim) { 1199 // Rethrow it -- we already tried AnimationUtils and it failed. 1200 throw e; 1201 } 1202 // Otherwise, it is probably an animation resource 1203 animation = AnimationUtils.loadAnimation(mHost.getContext(), nextAnim); 1204 if (animation != null) { 1205 return new AnimationOrAnimator(animation); 1206 } 1207 } 1208 } 1209 } 1210 1211 if (transit == 0) { 1212 return null; 1213 } 1214 1215 int styleIndex = transitToStyleIndex(transit, enter); 1216 if (styleIndex < 0) { 1217 return null; 1218 } 1219 1220 switch (styleIndex) { 1221 case ANIM_STYLE_OPEN_ENTER: 1222 return makeOpenCloseAnimation(mHost.getContext(), 1.125f, 1.0f, 0, 1); 1223 case ANIM_STYLE_OPEN_EXIT: 1224 return makeOpenCloseAnimation(mHost.getContext(), 1.0f, .975f, 1, 0); 1225 case ANIM_STYLE_CLOSE_ENTER: 1226 return makeOpenCloseAnimation(mHost.getContext(), .975f, 1.0f, 0, 1); 1227 case ANIM_STYLE_CLOSE_EXIT: 1228 return makeOpenCloseAnimation(mHost.getContext(), 1.0f, 1.075f, 1, 0); 1229 case ANIM_STYLE_FADE_ENTER: 1230 return makeFadeAnimation(mHost.getContext(), 0, 1); 1231 case ANIM_STYLE_FADE_EXIT: 1232 return makeFadeAnimation(mHost.getContext(), 1, 0); 1233 } 1234 1235 // TODO: remove or fix transitionStyle -- it apparently never worked. 1236 if (transitionStyle == 0 && mHost.onHasWindowAnimations()) { 1237 transitionStyle = mHost.onGetWindowAnimations(); 1238 } 1239 if (transitionStyle == 0) { 1240 return null; 1241 } 1242 1243 //TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle, 1244 // com.android.internal.R.styleable.FragmentAnimation); 1245 //int anim = attrs.getResourceId(styleIndex, 0); 1246 //attrs.recycle(); 1247 1248 //if (anim == 0) { 1249 // return null; 1250 //} 1251 1252 //return AnimatorInflater.loadAnimator(mActivity, anim); 1253 return null; 1254 } 1255 1256 public void performPendingDeferredStart(Fragment f) { 1257 if (f.mDeferStart) { 1258 if (mExecutingActions) { 1259 // Wait until we're done executing our pending transactions 1260 mHavePendingDeferredStart = true; 1261 return; 1262 } 1263 f.mDeferStart = false; 1264 moveToState(f, mCurState, 0, 0, false); 1265 } 1266 } 1267 1268 /** 1269 * Sets the to be animated view on hardware layer during the animation. Note 1270 * that calling this will replace any existing animation listener on the animation 1271 * with a new one, as animations do not support more than one listeners. Therefore, 1272 * animations that already have listeners should do the layer change operations 1273 * in their existing listeners, rather than calling this function. 1274 */ 1275 private static void setHWLayerAnimListenerIfAlpha(final View v, AnimationOrAnimator anim) { 1276 if (v == null || anim == null) { 1277 return; 1278 } 1279 if (shouldRunOnHWLayer(v, anim)) { 1280 if (anim.animator != null) { 1281 anim.animator.addListener(new AnimatorOnHWLayerIfNeededListener(v)); 1282 } else { 1283 AnimationListener originalListener = getAnimationListener(anim.animation); 1284 // If there's already a listener set on the animation, we need wrap the new listener 1285 // around the existing listener, so that they will both get animation listener 1286 // callbacks. 1287 v.setLayerType(View.LAYER_TYPE_HARDWARE, null); 1288 anim.animation.setAnimationListener(new AnimateOnHWLayerIfNeededListener(v, 1289 originalListener)); 1290 } 1291 } 1292 } 1293 1294 /** 1295 * Returns an existing AnimationListener on an Animation or {@code null} if none exists. 1296 */ 1297 private static AnimationListener getAnimationListener(Animation animation) { 1298 AnimationListener originalListener = null; 1299 try { 1300 if (sAnimationListenerField == null) { 1301 sAnimationListenerField = Animation.class.getDeclaredField("mListener"); 1302 sAnimationListenerField.setAccessible(true); 1303 } 1304 originalListener = (AnimationListener) sAnimationListenerField.get(animation); 1305 } catch (NoSuchFieldException e) { 1306 Log.e(TAG, "No field with the name mListener is found in Animation class", e); 1307 } catch (IllegalAccessException e) { 1308 Log.e(TAG, "Cannot access Animation's mListener field", e); 1309 } 1310 return originalListener; 1311 } 1312 1313 boolean isStateAtLeast(int state) { 1314 return mCurState >= state; 1315 } 1316 1317 @SuppressWarnings("ReferenceEquality") 1318 void moveToState(Fragment f, int newState, int transit, int transitionStyle, 1319 boolean keepActive) { 1320 // Fragments that are not currently added will sit in the onCreate() state. 1321 if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) { 1322 newState = Fragment.CREATED; 1323 } 1324 if (f.mRemoving && newState > f.mState) { 1325 if (f.mState == Fragment.INITIALIZING && f.isInBackStack()) { 1326 // Allow the fragment to be created so that it can be saved later. 1327 newState = Fragment.CREATED; 1328 } else { 1329 // While removing a fragment, we can't change it to a higher state. 1330 newState = f.mState; 1331 } 1332 } 1333 // Defer start if requested; don't allow it to move to STARTED or higher 1334 // if it's not already started. 1335 if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) { 1336 newState = Fragment.STOPPED; 1337 } 1338 if (f.mState <= newState) { 1339 // For fragments that are created from a layout, when restoring from 1340 // state we don't want to allow them to be created until they are 1341 // being reloaded from the layout. 1342 if (f.mFromLayout && !f.mInLayout) { 1343 return; 1344 } 1345 if (f.getAnimatingAway() != null || f.getAnimator() != null) { 1346 // The fragment is currently being animated... but! Now we 1347 // want to move our state back up. Give up on waiting for the 1348 // animation, move to whatever the final state should be once 1349 // the animation is done, and then we can proceed from there. 1350 f.setAnimatingAway(null); 1351 f.setAnimator(null); 1352 moveToState(f, f.getStateAfterAnimating(), 0, 0, true); 1353 } 1354 switch (f.mState) { 1355 case Fragment.INITIALIZING: 1356 if (newState > Fragment.INITIALIZING) { 1357 if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); 1358 if (f.mSavedFragmentState != null) { 1359 f.mSavedFragmentState.setClassLoader(mHost.getContext() 1360 .getClassLoader()); 1361 f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( 1362 FragmentManagerImpl.VIEW_STATE_TAG); 1363 f.mTarget = getFragment(f.mSavedFragmentState, 1364 FragmentManagerImpl.TARGET_STATE_TAG); 1365 if (f.mTarget != null) { 1366 f.mTargetRequestCode = f.mSavedFragmentState.getInt( 1367 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); 1368 } 1369 f.mUserVisibleHint = f.mSavedFragmentState.getBoolean( 1370 FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true); 1371 if (!f.mUserVisibleHint) { 1372 f.mDeferStart = true; 1373 if (newState > Fragment.STOPPED) { 1374 newState = Fragment.STOPPED; 1375 } 1376 } 1377 } 1378 1379 f.mHost = mHost; 1380 f.mParentFragment = mParent; 1381 f.mFragmentManager = mParent != null 1382 ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl(); 1383 1384 // If we have a target fragment, push it along to at least CREATED 1385 // so that this one can rely on it as an initialized dependency. 1386 if (f.mTarget != null) { 1387 if (mActive.get(f.mTarget.mIndex) != f.mTarget) { 1388 throw new IllegalStateException("Fragment " + f 1389 + " declared target fragment " + f.mTarget 1390 + " that does not belong to this FragmentManager!"); 1391 } 1392 if (f.mTarget.mState < Fragment.CREATED) { 1393 moveToState(f.mTarget, Fragment.CREATED, 0, 0, true); 1394 } 1395 } 1396 1397 dispatchOnFragmentPreAttached(f, mHost.getContext(), false); 1398 f.mCalled = false; 1399 f.onAttach(mHost.getContext()); 1400 if (!f.mCalled) { 1401 throw new SuperNotCalledException("Fragment " + f 1402 + " did not call through to super.onAttach()"); 1403 } 1404 if (f.mParentFragment == null) { 1405 mHost.onAttachFragment(f); 1406 } else { 1407 f.mParentFragment.onAttachFragment(f); 1408 } 1409 dispatchOnFragmentAttached(f, mHost.getContext(), false); 1410 1411 if (!f.mIsCreated) { 1412 dispatchOnFragmentPreCreated(f, f.mSavedFragmentState, false); 1413 f.performCreate(f.mSavedFragmentState); 1414 dispatchOnFragmentCreated(f, f.mSavedFragmentState, false); 1415 } else { 1416 f.restoreChildFragmentState(f.mSavedFragmentState); 1417 f.mState = Fragment.CREATED; 1418 } 1419 f.mRetaining = false; 1420 } 1421 // fall through 1422 case Fragment.CREATED: 1423 // This is outside the if statement below on purpose; we want this to run 1424 // even if we do a moveToState from CREATED => *, CREATED => CREATED, and 1425 // * => CREATED as part of the case fallthrough above. 1426 ensureInflatedFragmentView(f); 1427 1428 if (newState > Fragment.CREATED) { 1429 if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f); 1430 if (!f.mFromLayout) { 1431 ViewGroup container = null; 1432 if (f.mContainerId != 0) { 1433 if (f.mContainerId == View.NO_ID) { 1434 throwException(new IllegalArgumentException( 1435 "Cannot create fragment " 1436 + f 1437 + " for a container view with no id")); 1438 } 1439 container = (ViewGroup) mContainer.onFindViewById(f.mContainerId); 1440 if (container == null && !f.mRestored) { 1441 String resName; 1442 try { 1443 resName = f.getResources().getResourceName(f.mContainerId); 1444 } catch (NotFoundException e) { 1445 resName = "unknown"; 1446 } 1447 throwException(new IllegalArgumentException( 1448 "No view found for id 0x" 1449 + Integer.toHexString(f.mContainerId) + " (" 1450 + resName 1451 + ") for fragment " + f)); 1452 } 1453 } 1454 f.mContainer = container; 1455 f.mView = f.performCreateView(f.performGetLayoutInflater( 1456 f.mSavedFragmentState), container, f.mSavedFragmentState); 1457 if (f.mView != null) { 1458 f.mInnerView = f.mView; 1459 f.mView.setSaveFromParentEnabled(false); 1460 if (container != null) { 1461 container.addView(f.mView); 1462 } 1463 if (f.mHidden) { 1464 f.mView.setVisibility(View.GONE); 1465 } 1466 f.onViewCreated(f.mView, f.mSavedFragmentState); 1467 dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, 1468 false); 1469 // Only animate the view if it is visible. This is done after 1470 // dispatchOnFragmentViewCreated in case visibility is changed 1471 f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE) 1472 && f.mContainer != null; 1473 } else { 1474 f.mInnerView = null; 1475 } 1476 } 1477 1478 f.performActivityCreated(f.mSavedFragmentState); 1479 dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false); 1480 if (f.mView != null) { 1481 f.restoreViewState(f.mSavedFragmentState); 1482 } 1483 f.mSavedFragmentState = null; 1484 } 1485 // fall through 1486 case Fragment.ACTIVITY_CREATED: 1487 if (newState > Fragment.ACTIVITY_CREATED) { 1488 f.mState = Fragment.STOPPED; 1489 } 1490 // fall through 1491 case Fragment.STOPPED: 1492 if (newState > Fragment.STOPPED) { 1493 if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); 1494 f.performStart(); 1495 dispatchOnFragmentStarted(f, false); 1496 } 1497 // fall through 1498 case Fragment.STARTED: 1499 if (newState > Fragment.STARTED) { 1500 if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); 1501 f.performResume(); 1502 dispatchOnFragmentResumed(f, false); 1503 f.mSavedFragmentState = null; 1504 f.mSavedViewState = null; 1505 } 1506 } 1507 } else if (f.mState > newState) { 1508 switch (f.mState) { 1509 case Fragment.RESUMED: 1510 if (newState < Fragment.RESUMED) { 1511 if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); 1512 f.performPause(); 1513 dispatchOnFragmentPaused(f, false); 1514 } 1515 // fall through 1516 case Fragment.STARTED: 1517 if (newState < Fragment.STARTED) { 1518 if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); 1519 f.performStop(); 1520 dispatchOnFragmentStopped(f, false); 1521 } 1522 // fall through 1523 case Fragment.STOPPED: 1524 if (newState < Fragment.STOPPED) { 1525 if (DEBUG) Log.v(TAG, "movefrom STOPPED: " + f); 1526 f.performReallyStop(); 1527 } 1528 // fall through 1529 case Fragment.ACTIVITY_CREATED: 1530 if (newState < Fragment.ACTIVITY_CREATED) { 1531 if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f); 1532 if (f.mView != null) { 1533 // Need to save the current view state if not 1534 // done already. 1535 if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) { 1536 saveFragmentViewState(f); 1537 } 1538 } 1539 f.performDestroyView(); 1540 dispatchOnFragmentViewDestroyed(f, false); 1541 if (f.mView != null && f.mContainer != null) { 1542 // Stop any current animations: 1543 f.mContainer.endViewTransition(f.mView); 1544 f.mView.clearAnimation(); 1545 AnimationOrAnimator anim = null; 1546 if (mCurState > Fragment.INITIALIZING && !mDestroyed 1547 && f.mView.getVisibility() == View.VISIBLE 1548 && f.mPostponedAlpha >= 0) { 1549 anim = loadAnimation(f, transit, false, 1550 transitionStyle); 1551 } 1552 f.mPostponedAlpha = 0; 1553 if (anim != null) { 1554 animateRemoveFragment(f, anim, newState); 1555 } 1556 f.mContainer.removeView(f.mView); 1557 } 1558 f.mContainer = null; 1559 f.mView = null; 1560 f.mInnerView = null; 1561 f.mInLayout = false; 1562 } 1563 // fall through 1564 case Fragment.CREATED: 1565 if (newState < Fragment.CREATED) { 1566 if (mDestroyed) { 1567 // The fragment's containing activity is 1568 // being destroyed, but this fragment is 1569 // currently animating away. Stop the 1570 // animation right now -- it is not needed, 1571 // and we can't wait any more on destroying 1572 // the fragment. 1573 if (f.getAnimatingAway() != null) { 1574 View v = f.getAnimatingAway(); 1575 f.setAnimatingAway(null); 1576 v.clearAnimation(); 1577 } else if (f.getAnimator() != null) { 1578 Animator animator = f.getAnimator(); 1579 f.setAnimator(null); 1580 animator.cancel(); 1581 } 1582 } 1583 if (f.getAnimatingAway() != null || f.getAnimator() != null) { 1584 // We are waiting for the fragment's view to finish 1585 // animating away. Just make a note of the state 1586 // the fragment now should move to once the animation 1587 // is done. 1588 f.setStateAfterAnimating(newState); 1589 newState = Fragment.CREATED; 1590 } else { 1591 if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); 1592 if (!f.mRetaining) { 1593 f.performDestroy(); 1594 dispatchOnFragmentDestroyed(f, false); 1595 } else { 1596 f.mState = Fragment.INITIALIZING; 1597 } 1598 1599 f.performDetach(); 1600 dispatchOnFragmentDetached(f, false); 1601 if (!keepActive) { 1602 if (!f.mRetaining) { 1603 makeInactive(f); 1604 } else { 1605 f.mHost = null; 1606 f.mParentFragment = null; 1607 f.mFragmentManager = null; 1608 } 1609 } 1610 } 1611 } 1612 } 1613 } 1614 1615 if (f.mState != newState) { 1616 Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; " 1617 + "expected state " + newState + " found " + f.mState); 1618 f.mState = newState; 1619 } 1620 } 1621 1622 /** 1623 * Animates the removal of a fragment with the given animator or animation. After animating, 1624 * the fragment's view will be removed from the hierarchy. 1625 * 1626 * @param fragment The fragment to animate out 1627 * @param anim The animator or animation to run on the fragment's view 1628 * @param newState The final state after animating. 1629 */ 1630 private void animateRemoveFragment(@NonNull final Fragment fragment, 1631 @NonNull AnimationOrAnimator anim, final int newState) { 1632 final View viewToAnimate = fragment.mView; 1633 final ViewGroup container = fragment.mContainer; 1634 container.startViewTransition(viewToAnimate); 1635 fragment.setStateAfterAnimating(newState); 1636 if (anim.animation != null) { 1637 Animation animation = 1638 new EndViewTransitionAnimator(anim.animation, container, viewToAnimate); 1639 fragment.setAnimatingAway(fragment.mView); 1640 AnimationListener listener = getAnimationListener(animation); 1641 animation.setAnimationListener(new AnimationListenerWrapper(listener) { 1642 @Override 1643 public void onAnimationEnd(Animation animation) { 1644 super.onAnimationEnd(animation); 1645 1646 // onAnimationEnd() comes during draw(), so there can still be some 1647 // draw events happening after this call. We don't want to detach 1648 // the view until after the onAnimationEnd() 1649 container.post(new Runnable() { 1650 @Override 1651 public void run() { 1652 if (fragment.getAnimatingAway() != null) { 1653 fragment.setAnimatingAway(null); 1654 moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, 1655 false); 1656 } 1657 } 1658 }); 1659 } 1660 }); 1661 setHWLayerAnimListenerIfAlpha(viewToAnimate, anim); 1662 fragment.mView.startAnimation(animation); 1663 } else { 1664 Animator animator = anim.animator; 1665 fragment.setAnimator(anim.animator); 1666 animator.addListener(new AnimatorListenerAdapter() { 1667 @Override 1668 public void onAnimationEnd(Animator anim) { 1669 container.endViewTransition(viewToAnimate); 1670 // If an animator ends immediately, we can just pretend there is no animation. 1671 // When that happens the the fragment's view won't have been removed yet. 1672 Animator animator = fragment.getAnimator(); 1673 fragment.setAnimator(null); 1674 if (animator != null && container.indexOfChild(viewToAnimate) < 0) { 1675 moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, false); 1676 } 1677 } 1678 }); 1679 animator.setTarget(fragment.mView); 1680 setHWLayerAnimListenerIfAlpha(fragment.mView, anim); 1681 animator.start(); 1682 } 1683 } 1684 1685 void moveToState(Fragment f) { 1686 moveToState(f, mCurState, 0, 0, false); 1687 } 1688 1689 void ensureInflatedFragmentView(Fragment f) { 1690 if (f.mFromLayout && !f.mPerformedCreateView) { 1691 f.mView = f.performCreateView(f.performGetLayoutInflater( 1692 f.mSavedFragmentState), null, f.mSavedFragmentState); 1693 if (f.mView != null) { 1694 f.mInnerView = f.mView; 1695 f.mView.setSaveFromParentEnabled(false); 1696 if (f.mHidden) f.mView.setVisibility(View.GONE); 1697 f.onViewCreated(f.mView, f.mSavedFragmentState); 1698 dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState, false); 1699 } else { 1700 f.mInnerView = null; 1701 } 1702 } 1703 } 1704 1705 /** 1706 * Fragments that have been shown or hidden don't have their visibility changed or 1707 * animations run during the {@link #showFragment(Fragment)} or {@link #hideFragment(Fragment)} 1708 * calls. After fragments are brought to their final state in 1709 * {@link #moveFragmentToExpectedState(Fragment)} the fragments that have been shown or 1710 * hidden must have their visibility changed and their animations started here. 1711 * 1712 * @param fragment The fragment with mHiddenChanged = true that should change its View's 1713 * visibility and start the show or hide animation. 1714 */ 1715 void completeShowHideFragment(final Fragment fragment) { 1716 if (fragment.mView != null) { 1717 AnimationOrAnimator anim = loadAnimation(fragment, fragment.getNextTransition(), 1718 !fragment.mHidden, fragment.getNextTransitionStyle()); 1719 if (anim != null && anim.animator != null) { 1720 anim.animator.setTarget(fragment.mView); 1721 if (fragment.mHidden) { 1722 if (fragment.isHideReplaced()) { 1723 fragment.setHideReplaced(false); 1724 } else { 1725 final ViewGroup container = fragment.mContainer; 1726 final View animatingView = fragment.mView; 1727 container.startViewTransition(animatingView); 1728 // Delay the actual hide operation until the animation finishes, 1729 // otherwise the fragment will just immediately disappear 1730 anim.animator.addListener(new AnimatorListenerAdapter() { 1731 @Override 1732 public void onAnimationEnd(Animator animation) { 1733 container.endViewTransition(animatingView); 1734 animation.removeListener(this); 1735 if (fragment.mView != null) { 1736 fragment.mView.setVisibility(View.GONE); 1737 } 1738 } 1739 }); 1740 } 1741 } else { 1742 fragment.mView.setVisibility(View.VISIBLE); 1743 } 1744 setHWLayerAnimListenerIfAlpha(fragment.mView, anim); 1745 anim.animator.start(); 1746 } else { 1747 if (anim != null) { 1748 setHWLayerAnimListenerIfAlpha(fragment.mView, anim); 1749 fragment.mView.startAnimation(anim.animation); 1750 anim.animation.start(); 1751 } 1752 final int visibility = fragment.mHidden && !fragment.isHideReplaced() 1753 ? View.GONE 1754 : View.VISIBLE; 1755 fragment.mView.setVisibility(visibility); 1756 if (fragment.isHideReplaced()) { 1757 fragment.setHideReplaced(false); 1758 } 1759 } 1760 } 1761 if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { 1762 mNeedMenuInvalidate = true; 1763 } 1764 fragment.mHiddenChanged = false; 1765 fragment.onHiddenChanged(fragment.mHidden); 1766 } 1767 1768 /** 1769 * Moves a fragment to its expected final state or the fragment manager's state, depending 1770 * on whether the fragment manager's state is raised properly. 1771 * 1772 * @param f The fragment to change. 1773 */ 1774 void moveFragmentToExpectedState(Fragment f) { 1775 if (f == null) { 1776 return; 1777 } 1778 int nextState = mCurState; 1779 if (f.mRemoving) { 1780 if (f.isInBackStack()) { 1781 nextState = Math.min(nextState, Fragment.CREATED); 1782 } else { 1783 nextState = Math.min(nextState, Fragment.INITIALIZING); 1784 } 1785 } 1786 moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false); 1787 1788 if (f.mView != null) { 1789 // Move the view if it is out of order 1790 Fragment underFragment = findFragmentUnder(f); 1791 if (underFragment != null) { 1792 final View underView = underFragment.mView; 1793 // make sure this fragment is in the right order. 1794 final ViewGroup container = f.mContainer; 1795 int underIndex = container.indexOfChild(underView); 1796 int viewIndex = container.indexOfChild(f.mView); 1797 if (viewIndex < underIndex) { 1798 container.removeViewAt(viewIndex); 1799 container.addView(f.mView, underIndex); 1800 } 1801 } 1802 if (f.mIsNewlyAdded && f.mContainer != null) { 1803 // Make it visible and run the animations 1804 if (f.mPostponedAlpha > 0f) { 1805 f.mView.setAlpha(f.mPostponedAlpha); 1806 } 1807 f.mPostponedAlpha = 0f; 1808 f.mIsNewlyAdded = false; 1809 // run animations: 1810 AnimationOrAnimator anim = loadAnimation(f, f.getNextTransition(), true, 1811 f.getNextTransitionStyle()); 1812 if (anim != null) { 1813 setHWLayerAnimListenerIfAlpha(f.mView, anim); 1814 if (anim.animation != null) { 1815 f.mView.startAnimation(anim.animation); 1816 } else { 1817 anim.animator.setTarget(f.mView); 1818 anim.animator.start(); 1819 } 1820 } 1821 } 1822 } 1823 if (f.mHiddenChanged) { 1824 completeShowHideFragment(f); 1825 } 1826 } 1827 1828 /** 1829 * Changes the state of the fragment manager to {@code newState}. If the fragment manager 1830 * changes state or {@code always} is {@code true}, any fragments within it have their 1831 * states updated as well. 1832 * 1833 * @param newState The new state for the fragment manager 1834 * @param always If {@code true}, all fragments update their state, even 1835 * if {@code newState} matches the current fragment manager's state. 1836 */ 1837 void moveToState(int newState, boolean always) { 1838 if (mHost == null && newState != Fragment.INITIALIZING) { 1839 throw new IllegalStateException("No activity"); 1840 } 1841 1842 if (!always && newState == mCurState) { 1843 return; 1844 } 1845 1846 mCurState = newState; 1847 1848 if (mActive != null) { 1849 1850 // Must add them in the proper order. mActive fragments may be out of order 1851 final int numAdded = mAdded.size(); 1852 for (int i = 0; i < numAdded; i++) { 1853 Fragment f = mAdded.get(i); 1854 moveFragmentToExpectedState(f); 1855 } 1856 1857 // Now iterate through all active fragments. These will include those that are removed 1858 // and detached. 1859 final int numActive = mActive.size(); 1860 for (int i = 0; i < numActive; i++) { 1861 Fragment f = mActive.valueAt(i); 1862 if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) { 1863 moveFragmentToExpectedState(f); 1864 } 1865 } 1866 1867 startPendingDeferredFragments(); 1868 1869 if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) { 1870 mHost.onSupportInvalidateOptionsMenu(); 1871 mNeedMenuInvalidate = false; 1872 } 1873 } 1874 } 1875 1876 void startPendingDeferredFragments() { 1877 if (mActive == null) return; 1878 1879 for (int i=0; i<mActive.size(); i++) { 1880 Fragment f = mActive.valueAt(i); 1881 if (f != null) { 1882 performPendingDeferredStart(f); 1883 } 1884 } 1885 } 1886 1887 void makeActive(Fragment f) { 1888 if (f.mIndex >= 0) { 1889 return; 1890 } 1891 1892 f.setIndex(mNextFragmentIndex++, mParent); 1893 if (mActive == null) { 1894 mActive = new SparseArray<>(); 1895 } 1896 mActive.put(f.mIndex, f); 1897 if (DEBUG) Log.v(TAG, "Allocated fragment index " + f); 1898 } 1899 1900 void makeInactive(Fragment f) { 1901 if (f.mIndex < 0) { 1902 return; 1903 } 1904 1905 if (DEBUG) Log.v(TAG, "Freeing fragment index " + f); 1906 // Don't remove yet. That happens in burpActive(). This prevents 1907 // concurrent modification while iterating over mActive 1908 mActive.put(f.mIndex, null); 1909 1910 f.initState(); 1911 } 1912 1913 public void addFragment(Fragment fragment, boolean moveToStateNow) { 1914 if (DEBUG) Log.v(TAG, "add: " + fragment); 1915 makeActive(fragment); 1916 if (!fragment.mDetached) { 1917 if (mAdded.contains(fragment)) { 1918 throw new IllegalStateException("Fragment already added: " + fragment); 1919 } 1920 synchronized (mAdded) { 1921 mAdded.add(fragment); 1922 } 1923 fragment.mAdded = true; 1924 fragment.mRemoving = false; 1925 if (fragment.mView == null) { 1926 fragment.mHiddenChanged = false; 1927 } 1928 if (fragment.mHasMenu && fragment.mMenuVisible) { 1929 mNeedMenuInvalidate = true; 1930 } 1931 if (moveToStateNow) { 1932 moveToState(fragment); 1933 } 1934 } 1935 } 1936 1937 public void removeFragment(Fragment fragment) { 1938 if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); 1939 final boolean inactive = !fragment.isInBackStack(); 1940 if (!fragment.mDetached || inactive) { 1941 synchronized (mAdded) { 1942 mAdded.remove(fragment); 1943 } 1944 if (fragment.mHasMenu && fragment.mMenuVisible) { 1945 mNeedMenuInvalidate = true; 1946 } 1947 fragment.mAdded = false; 1948 fragment.mRemoving = true; 1949 } 1950 } 1951 1952 /** 1953 * Marks a fragment as hidden to be later animated in with 1954 * {@link #completeShowHideFragment(Fragment)}. 1955 * 1956 * @param fragment The fragment to be shown. 1957 */ 1958 public void hideFragment(Fragment fragment) { 1959 if (DEBUG) Log.v(TAG, "hide: " + fragment); 1960 if (!fragment.mHidden) { 1961 fragment.mHidden = true; 1962 // Toggle hidden changed so that if a fragment goes through show/hide/show 1963 // it doesn't go through the animation. 1964 fragment.mHiddenChanged = !fragment.mHiddenChanged; 1965 } 1966 } 1967 1968 /** 1969 * Marks a fragment as shown to be later animated in with 1970 * {@link #completeShowHideFragment(Fragment)}. 1971 * 1972 * @param fragment The fragment to be shown. 1973 */ 1974 public void showFragment(Fragment fragment) { 1975 if (DEBUG) Log.v(TAG, "show: " + fragment); 1976 if (fragment.mHidden) { 1977 fragment.mHidden = false; 1978 // Toggle hidden changed so that if a fragment goes through show/hide/show 1979 // it doesn't go through the animation. 1980 fragment.mHiddenChanged = !fragment.mHiddenChanged; 1981 } 1982 } 1983 1984 public void detachFragment(Fragment fragment) { 1985 if (DEBUG) Log.v(TAG, "detach: " + fragment); 1986 if (!fragment.mDetached) { 1987 fragment.mDetached = true; 1988 if (fragment.mAdded) { 1989 // We are not already in back stack, so need to remove the fragment. 1990 if (DEBUG) Log.v(TAG, "remove from detach: " + fragment); 1991 synchronized (mAdded) { 1992 mAdded.remove(fragment); 1993 } 1994 if (fragment.mHasMenu && fragment.mMenuVisible) { 1995 mNeedMenuInvalidate = true; 1996 } 1997 fragment.mAdded = false; 1998 } 1999 } 2000 } 2001 2002 public void attachFragment(Fragment fragment) { 2003 if (DEBUG) Log.v(TAG, "attach: " + fragment); 2004 if (fragment.mDetached) { 2005 fragment.mDetached = false; 2006 if (!fragment.mAdded) { 2007 if (mAdded.contains(fragment)) { 2008 throw new IllegalStateException("Fragment already added: " + fragment); 2009 } 2010 if (DEBUG) Log.v(TAG, "add from attach: " + fragment); 2011 synchronized (mAdded) { 2012 mAdded.add(fragment); 2013 } 2014 fragment.mAdded = true; 2015 if (fragment.mHasMenu && fragment.mMenuVisible) { 2016 mNeedMenuInvalidate = true; 2017 } 2018 } 2019 } 2020 } 2021 2022 @Override 2023 @Nullable 2024 public Fragment findFragmentById(int id) { 2025 // First look through added fragments. 2026 for (int i = mAdded.size() - 1; i >= 0; i--) { 2027 Fragment f = mAdded.get(i); 2028 if (f != null && f.mFragmentId == id) { 2029 return f; 2030 } 2031 } 2032 if (mActive != null) { 2033 // Now for any known fragment. 2034 for (int i=mActive.size()-1; i>=0; i--) { 2035 Fragment f = mActive.valueAt(i); 2036 if (f != null && f.mFragmentId == id) { 2037 return f; 2038 } 2039 } 2040 } 2041 return null; 2042 } 2043 2044 @Override 2045 @Nullable 2046 public Fragment findFragmentByTag(@Nullable String tag) { 2047 if (tag != null) { 2048 // First look through added fragments. 2049 for (int i=mAdded.size()-1; i>=0; i--) { 2050 Fragment f = mAdded.get(i); 2051 if (f != null && tag.equals(f.mTag)) { 2052 return f; 2053 } 2054 } 2055 } 2056 if (mActive != null && tag != null) { 2057 // Now for any known fragment. 2058 for (int i=mActive.size()-1; i>=0; i--) { 2059 Fragment f = mActive.valueAt(i); 2060 if (f != null && tag.equals(f.mTag)) { 2061 return f; 2062 } 2063 } 2064 } 2065 return null; 2066 } 2067 2068 public Fragment findFragmentByWho(String who) { 2069 if (mActive != null && who != null) { 2070 for (int i=mActive.size()-1; i>=0; i--) { 2071 Fragment f = mActive.valueAt(i); 2072 if (f != null && (f=f.findFragmentByWho(who)) != null) { 2073 return f; 2074 } 2075 } 2076 } 2077 return null; 2078 } 2079 2080 private void checkStateLoss() { 2081 if (isStateSaved()) { 2082 throw new IllegalStateException( 2083 "Can not perform this action after onSaveInstanceState"); 2084 } 2085 if (mNoTransactionsBecause != null) { 2086 throw new IllegalStateException( 2087 "Can not perform this action inside of " + mNoTransactionsBecause); 2088 } 2089 } 2090 2091 @Override 2092 public boolean isStateSaved() { 2093 // See saveAllState() for the explanation of this. We do this for 2094 // all platform versions, to keep our behavior more consistent between 2095 // them. 2096 return mStateSaved || mStopped; 2097 } 2098 2099 /** 2100 * Adds an action to the queue of pending actions. 2101 * 2102 * @param action the action to add 2103 * @param allowStateLoss whether to allow loss of state information 2104 * @throws IllegalStateException if the activity has been destroyed 2105 */ 2106 public void enqueueAction(OpGenerator action, boolean allowStateLoss) { 2107 if (!allowStateLoss) { 2108 checkStateLoss(); 2109 } 2110 synchronized (this) { 2111 if (mDestroyed || mHost == null) { 2112 if (allowStateLoss) { 2113 // This FragmentManager isn't attached, so drop the entire transaction. 2114 return; 2115 } 2116 throw new IllegalStateException("Activity has been destroyed"); 2117 } 2118 if (mPendingActions == null) { 2119 mPendingActions = new ArrayList<>(); 2120 } 2121 mPendingActions.add(action); 2122 scheduleCommit(); 2123 } 2124 } 2125 2126 /** 2127 * Schedules the execution when one hasn't been scheduled already. This should happen 2128 * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when 2129 * a postponed transaction has been started with 2130 * {@link Fragment#startPostponedEnterTransition()} 2131 */ 2132 private void scheduleCommit() { 2133 synchronized (this) { 2134 boolean postponeReady = 2135 mPostponedTransactions != null && !mPostponedTransactions.isEmpty(); 2136 boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1; 2137 if (postponeReady || pendingReady) { 2138 mHost.getHandler().removeCallbacks(mExecCommit); 2139 mHost.getHandler().post(mExecCommit); 2140 } 2141 } 2142 } 2143 2144 public int allocBackStackIndex(BackStackRecord bse) { 2145 synchronized (this) { 2146 if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { 2147 if (mBackStackIndices == null) { 2148 mBackStackIndices = new ArrayList<BackStackRecord>(); 2149 } 2150 int index = mBackStackIndices.size(); 2151 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 2152 mBackStackIndices.add(bse); 2153 return index; 2154 2155 } else { 2156 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); 2157 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 2158 mBackStackIndices.set(index, bse); 2159 return index; 2160 } 2161 } 2162 } 2163 2164 public void setBackStackIndex(int index, BackStackRecord bse) { 2165 synchronized (this) { 2166 if (mBackStackIndices == null) { 2167 mBackStackIndices = new ArrayList<BackStackRecord>(); 2168 } 2169 int N = mBackStackIndices.size(); 2170 if (index < N) { 2171 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 2172 mBackStackIndices.set(index, bse); 2173 } else { 2174 while (N < index) { 2175 mBackStackIndices.add(null); 2176 if (mAvailBackStackIndices == null) { 2177 mAvailBackStackIndices = new ArrayList<Integer>(); 2178 } 2179 if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); 2180 mAvailBackStackIndices.add(N); 2181 N++; 2182 } 2183 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 2184 mBackStackIndices.add(bse); 2185 } 2186 } 2187 } 2188 2189 public void freeBackStackIndex(int index) { 2190 synchronized (this) { 2191 mBackStackIndices.set(index, null); 2192 if (mAvailBackStackIndices == null) { 2193 mAvailBackStackIndices = new ArrayList<Integer>(); 2194 } 2195 if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); 2196 mAvailBackStackIndices.add(index); 2197 } 2198 } 2199 2200 /** 2201 * Broken out from exec*, this prepares for gathering and executing operations. 2202 * 2203 * @param allowStateLoss true if state loss should be ignored or false if it should be 2204 * checked. 2205 */ 2206 private void ensureExecReady(boolean allowStateLoss) { 2207 if (mExecutingActions) { 2208 throw new IllegalStateException("FragmentManager is already executing transactions"); 2209 } 2210 2211 if (mHost == null) { 2212 throw new IllegalStateException("Fragment host has been destroyed"); 2213 } 2214 2215 if (Looper.myLooper() != mHost.getHandler().getLooper()) { 2216 throw new IllegalStateException("Must be called from main thread of fragment host"); 2217 } 2218 2219 if (!allowStateLoss) { 2220 checkStateLoss(); 2221 } 2222 2223 if (mTmpRecords == null) { 2224 mTmpRecords = new ArrayList<>(); 2225 mTmpIsPop = new ArrayList<>(); 2226 } 2227 mExecutingActions = true; 2228 try { 2229 executePostponedTransaction(null, null); 2230 } finally { 2231 mExecutingActions = false; 2232 } 2233 } 2234 2235 public void execSingleAction(OpGenerator action, boolean allowStateLoss) { 2236 if (allowStateLoss && (mHost == null || mDestroyed)) { 2237 // This FragmentManager isn't attached, so drop the entire transaction. 2238 return; 2239 } 2240 ensureExecReady(allowStateLoss); 2241 if (action.generateOps(mTmpRecords, mTmpIsPop)) { 2242 mExecutingActions = true; 2243 try { 2244 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop); 2245 } finally { 2246 cleanupExec(); 2247 } 2248 } 2249 2250 doPendingDeferredStart(); 2251 burpActive(); 2252 } 2253 2254 /** 2255 * Broken out of exec*, this cleans up the mExecutingActions and the temporary structures 2256 * used in executing operations. 2257 */ 2258 private void cleanupExec() { 2259 mExecutingActions = false; 2260 mTmpIsPop.clear(); 2261 mTmpRecords.clear(); 2262 } 2263 2264 /** 2265 * Only call from main thread! 2266 */ 2267 public boolean execPendingActions() { 2268 ensureExecReady(true); 2269 2270 boolean didSomething = false; 2271 while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) { 2272 mExecutingActions = true; 2273 try { 2274 removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop); 2275 } finally { 2276 cleanupExec(); 2277 } 2278 didSomething = true; 2279 } 2280 2281 doPendingDeferredStart(); 2282 burpActive(); 2283 2284 return didSomething; 2285 } 2286 2287 /** 2288 * Complete the execution of transactions that have previously been postponed, but are 2289 * now ready. 2290 */ 2291 private void executePostponedTransaction(ArrayList<BackStackRecord> records, 2292 ArrayList<Boolean> isRecordPop) { 2293 int numPostponed = mPostponedTransactions == null ? 0 : mPostponedTransactions.size(); 2294 for (int i = 0; i < numPostponed; i++) { 2295 StartEnterTransitionListener listener = mPostponedTransactions.get(i); 2296 if (records != null && !listener.mIsBack) { 2297 int index = records.indexOf(listener.mRecord); 2298 if (index != -1 && isRecordPop.get(index)) { 2299 listener.cancelTransaction(); 2300 continue; 2301 } 2302 } 2303 if (listener.isReady() || (records != null 2304 && listener.mRecord.interactsWith(records, 0, records.size()))) { 2305 mPostponedTransactions.remove(i); 2306 i--; 2307 numPostponed--; 2308 int index; 2309 if (records != null && !listener.mIsBack 2310 && (index = records.indexOf(listener.mRecord)) != -1 2311 && isRecordPop.get(index)) { 2312 // This is popping a postponed transaction 2313 listener.cancelTransaction(); 2314 } else { 2315 listener.completeTransaction(); 2316 } 2317 } 2318 } 2319 } 2320 2321 /** 2322 * Remove redundant BackStackRecord operations and executes them. This method merges operations 2323 * of proximate records that allow reordering. See 2324 * {@link FragmentTransaction#setReorderingAllowed(boolean)}. 2325 * <p> 2326 * For example, a transaction that adds to the back stack and then another that pops that 2327 * back stack record will be optimized to remove the unnecessary operation. 2328 * <p> 2329 * Likewise, two transactions committed that are executed at the same time will be optimized 2330 * to remove the redundant operations as well as two pop operations executed together. 2331 * 2332 * @param records The records pending execution 2333 * @param isRecordPop The direction that these records are being run. 2334 */ 2335 private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records, 2336 ArrayList<Boolean> isRecordPop) { 2337 if (records == null || records.isEmpty()) { 2338 return; 2339 } 2340 2341 if (isRecordPop == null || records.size() != isRecordPop.size()) { 2342 throw new IllegalStateException("Internal error with the back stack records"); 2343 } 2344 2345 // Force start of any postponed transactions that interact with scheduled transactions: 2346 executePostponedTransaction(records, isRecordPop); 2347 2348 final int numRecords = records.size(); 2349 int startIndex = 0; 2350 for (int recordNum = 0; recordNum < numRecords; recordNum++) { 2351 final boolean canReorder = records.get(recordNum).mReorderingAllowed; 2352 if (!canReorder) { 2353 // execute all previous transactions 2354 if (startIndex != recordNum) { 2355 executeOpsTogether(records, isRecordPop, startIndex, recordNum); 2356 } 2357 // execute all pop operations that don't allow reordering together or 2358 // one add operation 2359 int reorderingEnd = recordNum + 1; 2360 if (isRecordPop.get(recordNum)) { 2361 while (reorderingEnd < numRecords 2362 && isRecordPop.get(reorderingEnd) 2363 && !records.get(reorderingEnd).mReorderingAllowed) { 2364 reorderingEnd++; 2365 } 2366 } 2367 executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd); 2368 startIndex = reorderingEnd; 2369 recordNum = reorderingEnd - 1; 2370 } 2371 } 2372 if (startIndex != numRecords) { 2373 executeOpsTogether(records, isRecordPop, startIndex, numRecords); 2374 } 2375 } 2376 2377 /** 2378 * Executes a subset of a list of BackStackRecords, all of which either allow reordering or 2379 * do not allow ordering. 2380 * @param records A list of BackStackRecords that are to be executed 2381 * @param isRecordPop The direction that these records are being run. 2382 * @param startIndex The index of the first record in <code>records</code> to be executed 2383 * @param endIndex One more than the final record index in <code>records</code> to executed. 2384 */ 2385 private void executeOpsTogether(ArrayList<BackStackRecord> records, 2386 ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) { 2387 final boolean allowReordering = records.get(startIndex).mReorderingAllowed; 2388 boolean addToBackStack = false; 2389 if (mTmpAddedFragments == null) { 2390 mTmpAddedFragments = new ArrayList<>(); 2391 } else { 2392 mTmpAddedFragments.clear(); 2393 } 2394 mTmpAddedFragments.addAll(mAdded); 2395 Fragment oldPrimaryNav = getPrimaryNavigationFragment(); 2396 for (int recordNum = startIndex; recordNum < endIndex; recordNum++) { 2397 final BackStackRecord record = records.get(recordNum); 2398 final boolean isPop = isRecordPop.get(recordNum); 2399 if (!isPop) { 2400 oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav); 2401 } else { 2402 oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav); 2403 } 2404 addToBackStack = addToBackStack || record.mAddToBackStack; 2405 } 2406 mTmpAddedFragments.clear(); 2407 2408 if (!allowReordering) { 2409 FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, endIndex, 2410 false); 2411 } 2412 executeOps(records, isRecordPop, startIndex, endIndex); 2413 2414 int postponeIndex = endIndex; 2415 if (allowReordering) { 2416 ArraySet<Fragment> addedFragments = new ArraySet<>(); 2417 addAddedFragments(addedFragments); 2418 postponeIndex = postponePostponableTransactions(records, isRecordPop, 2419 startIndex, endIndex, addedFragments); 2420 makeRemovedFragmentsInvisible(addedFragments); 2421 } 2422 2423 if (postponeIndex != startIndex && allowReordering) { 2424 // need to run something now 2425 FragmentTransition.startTransitions(this, records, isRecordPop, startIndex, 2426 postponeIndex, true); 2427 moveToState(mCurState, true); 2428 } 2429 2430 for (int recordNum = startIndex; recordNum < endIndex; recordNum++) { 2431 final BackStackRecord record = records.get(recordNum); 2432 final boolean isPop = isRecordPop.get(recordNum); 2433 if (isPop && record.mIndex >= 0) { 2434 freeBackStackIndex(record.mIndex); 2435 record.mIndex = -1; 2436 } 2437 record.runOnCommitRunnables(); 2438 } 2439 if (addToBackStack) { 2440 reportBackStackChanged(); 2441 } 2442 } 2443 2444 /** 2445 * Any fragments that were removed because they have been postponed should have their views 2446 * made invisible by setting their alpha to 0. 2447 * 2448 * @param fragments The fragments that were added during operation execution. Only the ones 2449 * that are no longer added will have their alpha changed. 2450 */ 2451 private void makeRemovedFragmentsInvisible(ArraySet<Fragment> fragments) { 2452 final int numAdded = fragments.size(); 2453 for (int i = 0; i < numAdded; i++) { 2454 final Fragment fragment = fragments.valueAt(i); 2455 if (!fragment.mAdded) { 2456 final View view = fragment.getView(); 2457 fragment.mPostponedAlpha = view.getAlpha(); 2458 view.setAlpha(0f); 2459 } 2460 } 2461 } 2462 2463 /** 2464 * Examine all transactions and determine which ones are marked as postponed. Those will 2465 * have their operations rolled back and moved to the end of the record list (up to endIndex). 2466 * It will also add the postponed transaction to the queue. 2467 * 2468 * @param records A list of BackStackRecords that should be checked. 2469 * @param isRecordPop The direction that these records are being run. 2470 * @param startIndex The index of the first record in <code>records</code> to be checked 2471 * @param endIndex One more than the final record index in <code>records</code> to be checked. 2472 * @return The index of the first postponed transaction or endIndex if no transaction was 2473 * postponed. 2474 */ 2475 private int postponePostponableTransactions(ArrayList<BackStackRecord> records, 2476 ArrayList<Boolean> isRecordPop, int startIndex, int endIndex, 2477 ArraySet<Fragment> added) { 2478 int postponeIndex = endIndex; 2479 for (int i = endIndex - 1; i >= startIndex; i--) { 2480 final BackStackRecord record = records.get(i); 2481 final boolean isPop = isRecordPop.get(i); 2482 boolean isPostponed = record.isPostponed() 2483 && !record.interactsWith(records, i + 1, endIndex); 2484 if (isPostponed) { 2485 if (mPostponedTransactions == null) { 2486 mPostponedTransactions = new ArrayList<>(); 2487 } 2488 StartEnterTransitionListener listener = 2489 new StartEnterTransitionListener(record, isPop); 2490 mPostponedTransactions.add(listener); 2491 record.setOnStartPostponedListener(listener); 2492 2493 // roll back the transaction 2494 if (isPop) { 2495 record.executeOps(); 2496 } else { 2497 record.executePopOps(false); 2498 } 2499 2500 // move to the end 2501 postponeIndex--; 2502 if (i != postponeIndex) { 2503 records.remove(i); 2504 records.add(postponeIndex, record); 2505 } 2506 2507 // different views may be visible now 2508 addAddedFragments(added); 2509 } 2510 } 2511 return postponeIndex; 2512 } 2513 2514 /** 2515 * When a postponed transaction is ready to be started, this completes the transaction, 2516 * removing, hiding, or showing views as well as starting the animations and transitions. 2517 * <p> 2518 * {@code runtransitions} is set to false when the transaction postponement was interrupted 2519 * abnormally -- normally by a new transaction being started that affects the postponed 2520 * transaction. 2521 * 2522 * @param record The transaction to run 2523 * @param isPop true if record is popping or false if it is adding 2524 * @param runTransitions true if the fragment transition should be run or false otherwise. 2525 * @param moveToState true if the state should be changed after executing the operations. 2526 * This is false when the transaction is canceled when a postponed 2527 * transaction is popped. 2528 */ 2529 private void completeExecute(BackStackRecord record, boolean isPop, boolean runTransitions, 2530 boolean moveToState) { 2531 if (isPop) { 2532 record.executePopOps(moveToState); 2533 } else { 2534 record.executeOps(); 2535 } 2536 ArrayList<BackStackRecord> records = new ArrayList<>(1); 2537 ArrayList<Boolean> isRecordPop = new ArrayList<>(1); 2538 records.add(record); 2539 isRecordPop.add(isPop); 2540 if (runTransitions) { 2541 FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true); 2542 } 2543 if (moveToState) { 2544 moveToState(mCurState, true); 2545 } 2546 2547 if (mActive != null) { 2548 final int numActive = mActive.size(); 2549 for (int i = 0; i < numActive; i++) { 2550 // Allow added fragments to be removed during the pop since we aren't going 2551 // to move them to the final state with moveToState(mCurState). 2552 Fragment fragment = mActive.valueAt(i); 2553 if (fragment != null && fragment.mView != null && fragment.mIsNewlyAdded 2554 && record.interactsWith(fragment.mContainerId)) { 2555 if (fragment.mPostponedAlpha > 0) { 2556 fragment.mView.setAlpha(fragment.mPostponedAlpha); 2557 } 2558 if (moveToState) { 2559 fragment.mPostponedAlpha = 0; 2560 } else { 2561 fragment.mPostponedAlpha = -1; 2562 fragment.mIsNewlyAdded = false; 2563 } 2564 } 2565 } 2566 } 2567 } 2568 2569 /** 2570 * Find a fragment within the fragment's container whose View should be below the passed 2571 * fragment. {@code null} is returned when the fragment has no View or if there should be 2572 * no fragment with a View below the given fragment. 2573 * 2574 * As an example, if mAdded has two Fragments with Views sharing the same container: 2575 * FragmentA 2576 * FragmentB 2577 * 2578 * Then, when processing FragmentB, FragmentA will be returned. If, however, FragmentA 2579 * had no View, null would be returned. 2580 * 2581 * @param f The fragment that may be on top of another fragment. 2582 * @return The fragment with a View under f, if one exists or null if f has no View or 2583 * there are no fragments with Views in the same container. 2584 */ 2585 private Fragment findFragmentUnder(Fragment f) { 2586 final ViewGroup container = f.mContainer; 2587 final View view = f.mView; 2588 2589 if (container == null || view == null) { 2590 return null; 2591 } 2592 2593 final int fragmentIndex = mAdded.indexOf(f); 2594 for (int i = fragmentIndex - 1; i >= 0; i--) { 2595 Fragment underFragment = mAdded.get(i); 2596 if (underFragment.mContainer == container && underFragment.mView != null) { 2597 // Found the fragment under this one 2598 return underFragment; 2599 } 2600 } 2601 return null; 2602 } 2603 2604 /** 2605 * Run the operations in the BackStackRecords, either to push or pop. 2606 * 2607 * @param records The list of records whose operations should be run. 2608 * @param isRecordPop The direction that these records are being run. 2609 * @param startIndex The index of the first entry in records to run. 2610 * @param endIndex One past the index of the final entry in records to run. 2611 */ 2612 private static void executeOps(ArrayList<BackStackRecord> records, 2613 ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) { 2614 for (int i = startIndex; i < endIndex; i++) { 2615 final BackStackRecord record = records.get(i); 2616 final boolean isPop = isRecordPop.get(i); 2617 if (isPop) { 2618 record.bumpBackStackNesting(-1); 2619 // Only execute the add operations at the end of 2620 // all transactions. 2621 boolean moveToState = i == (endIndex - 1); 2622 record.executePopOps(moveToState); 2623 } else { 2624 record.bumpBackStackNesting(1); 2625 record.executeOps(); 2626 } 2627 } 2628 } 2629 2630 /** 2631 * Ensure that fragments that are added are moved to at least the CREATED state. 2632 * Any newly-added Views are inserted into {@code added} so that the Transaction can be 2633 * postponed with {@link Fragment#postponeEnterTransition()}. They will later be made 2634 * invisible (by setting their alpha to 0) if they have been removed when postponed. 2635 */ 2636 private void addAddedFragments(ArraySet<Fragment> added) { 2637 if (mCurState < Fragment.CREATED) { 2638 return; 2639 } 2640 // We want to leave the fragment in the started state 2641 final int state = Math.min(mCurState, Fragment.STARTED); 2642 final int numAdded = mAdded.size(); 2643 for (int i = 0; i < numAdded; i++) { 2644 Fragment fragment = mAdded.get(i); 2645 if (fragment.mState < state) { 2646 moveToState(fragment, state, fragment.getNextAnim(), fragment.getNextTransition(), 2647 false); 2648 if (fragment.mView != null && !fragment.mHidden && fragment.mIsNewlyAdded) { 2649 added.add(fragment); 2650 } 2651 } 2652 } 2653 } 2654 2655 /** 2656 * Starts all postponed transactions regardless of whether they are ready or not. 2657 */ 2658 private void forcePostponedTransactions() { 2659 if (mPostponedTransactions != null) { 2660 while (!mPostponedTransactions.isEmpty()) { 2661 mPostponedTransactions.remove(0).completeTransaction(); 2662 } 2663 } 2664 } 2665 2666 /** 2667 * Ends the animations of fragments so that they immediately reach the end state. 2668 * This is used prior to saving the state so that the correct state is saved. 2669 */ 2670 private void endAnimatingAwayFragments() { 2671 final int numFragments = mActive == null ? 0 : mActive.size(); 2672 for (int i = 0; i < numFragments; i++) { 2673 Fragment fragment = mActive.valueAt(i); 2674 if (fragment != null) { 2675 if (fragment.getAnimatingAway() != null) { 2676 // Give up waiting for the animation and just end it. 2677 final int stateAfterAnimating = fragment.getStateAfterAnimating(); 2678 final View animatingAway = fragment.getAnimatingAway(); 2679 Animation animation = animatingAway.getAnimation(); 2680 if (animation != null) { 2681 animation.cancel(); 2682 // force-clear the animation, as Animation#cancel() doesn't work prior to N, 2683 // and will instead cause the animation to infinitely loop 2684 animatingAway.clearAnimation(); 2685 } 2686 fragment.setAnimatingAway(null); 2687 moveToState(fragment, stateAfterAnimating, 0, 0, false); 2688 } else if (fragment.getAnimator() != null) { 2689 fragment.getAnimator().end(); 2690 } 2691 } 2692 } 2693 } 2694 2695 /** 2696 * Adds all records in the pending actions to records and whether they are add or pop 2697 * operations to isPop. After executing, the pending actions will be empty. 2698 * 2699 * @param records All pending actions will generate BackStackRecords added to this. 2700 * This contains the transactions, in order, to execute. 2701 * @param isPop All pending actions will generate booleans to add to this. This contains 2702 * an entry for each entry in records to indicate whether or not it is a 2703 * pop action. 2704 */ 2705 private boolean generateOpsForPendingActions(ArrayList<BackStackRecord> records, 2706 ArrayList<Boolean> isPop) { 2707 boolean didSomething = false; 2708 synchronized (this) { 2709 if (mPendingActions == null || mPendingActions.size() == 0) { 2710 return false; 2711 } 2712 2713 final int numActions = mPendingActions.size(); 2714 for (int i = 0; i < numActions; i++) { 2715 didSomething |= mPendingActions.get(i).generateOps(records, isPop); 2716 } 2717 mPendingActions.clear(); 2718 mHost.getHandler().removeCallbacks(mExecCommit); 2719 } 2720 return didSomething; 2721 } 2722 2723 void doPendingDeferredStart() { 2724 if (mHavePendingDeferredStart) { 2725 mHavePendingDeferredStart = false; 2726 startPendingDeferredFragments(); 2727 } 2728 } 2729 2730 void reportBackStackChanged() { 2731 if (mBackStackChangeListeners != null) { 2732 for (int i=0; i<mBackStackChangeListeners.size(); i++) { 2733 mBackStackChangeListeners.get(i).onBackStackChanged(); 2734 } 2735 } 2736 } 2737 2738 void addBackStackState(BackStackRecord state) { 2739 if (mBackStack == null) { 2740 mBackStack = new ArrayList<BackStackRecord>(); 2741 } 2742 mBackStack.add(state); 2743 } 2744 2745 @SuppressWarnings("unused") 2746 boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, 2747 String name, int id, int flags) { 2748 if (mBackStack == null) { 2749 return false; 2750 } 2751 if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) { 2752 int last = mBackStack.size() - 1; 2753 if (last < 0) { 2754 return false; 2755 } 2756 records.add(mBackStack.remove(last)); 2757 isRecordPop.add(true); 2758 } else { 2759 int index = -1; 2760 if (name != null || id >= 0) { 2761 // If a name or ID is specified, look for that place in 2762 // the stack. 2763 index = mBackStack.size()-1; 2764 while (index >= 0) { 2765 BackStackRecord bss = mBackStack.get(index); 2766 if (name != null && name.equals(bss.getName())) { 2767 break; 2768 } 2769 if (id >= 0 && id == bss.mIndex) { 2770 break; 2771 } 2772 index--; 2773 } 2774 if (index < 0) { 2775 return false; 2776 } 2777 if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { 2778 index--; 2779 // Consume all following entries that match. 2780 while (index >= 0) { 2781 BackStackRecord bss = mBackStack.get(index); 2782 if ((name != null && name.equals(bss.getName())) 2783 || (id >= 0 && id == bss.mIndex)) { 2784 index--; 2785 continue; 2786 } 2787 break; 2788 } 2789 } 2790 } 2791 if (index == mBackStack.size()-1) { 2792 return false; 2793 } 2794 for (int i = mBackStack.size() - 1; i > index; i--) { 2795 records.add(mBackStack.remove(i)); 2796 isRecordPop.add(true); 2797 } 2798 } 2799 return true; 2800 } 2801 2802 FragmentManagerNonConfig retainNonConfig() { 2803 setRetaining(mSavedNonConfig); 2804 return mSavedNonConfig; 2805 } 2806 2807 /** 2808 * Recurse the FragmentManagerNonConfig fragments and set the mRetaining to true. This 2809 * was previously done while saving the non-config state, but that has been moved to 2810 * {@link #saveNonConfig()} called from {@link #saveAllState()}. If mRetaining is set too 2811 * early, the fragment won't be destroyed when the FragmentManager is destroyed. 2812 */ 2813 private static void setRetaining(FragmentManagerNonConfig nonConfig) { 2814 if (nonConfig == null) { 2815 return; 2816 } 2817 List<Fragment> fragments = nonConfig.getFragments(); 2818 if (fragments != null) { 2819 for (Fragment fragment : fragments) { 2820 fragment.mRetaining = true; 2821 } 2822 } 2823 List<FragmentManagerNonConfig> children = nonConfig.getChildNonConfigs(); 2824 if (children != null) { 2825 for (FragmentManagerNonConfig child : children) { 2826 setRetaining(child); 2827 } 2828 } 2829 } 2830 2831 void saveNonConfig() { 2832 ArrayList<Fragment> fragments = null; 2833 ArrayList<FragmentManagerNonConfig> childFragments = null; 2834 ArrayList<ViewModelStore> viewModelStores = null; 2835 if (mActive != null) { 2836 for (int i=0; i<mActive.size(); i++) { 2837 Fragment f = mActive.valueAt(i); 2838 if (f != null) { 2839 if (f.mRetainInstance) { 2840 if (fragments == null) { 2841 fragments = new ArrayList<Fragment>(); 2842 } 2843 fragments.add(f); 2844 f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; 2845 if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f); 2846 } 2847 FragmentManagerNonConfig child; 2848 if (f.mChildFragmentManager != null) { 2849 f.mChildFragmentManager.saveNonConfig(); 2850 child = f.mChildFragmentManager.mSavedNonConfig; 2851 } else { 2852 // f.mChildNonConfig may be not null, when the parent fragment is 2853 // in the backstack. 2854 child = f.mChildNonConfig; 2855 } 2856 2857 if (childFragments == null && child != null) { 2858 childFragments = new ArrayList<>(mActive.size()); 2859 for (int j = 0; j < i; j++) { 2860 childFragments.add(null); 2861 } 2862 } 2863 2864 if (childFragments != null) { 2865 childFragments.add(child); 2866 } 2867 if (viewModelStores == null && f.mViewModelStore != null) { 2868 viewModelStores = new ArrayList<>(mActive.size()); 2869 for (int j = 0; j < i; j++) { 2870 viewModelStores.add(null); 2871 } 2872 } 2873 2874 if (viewModelStores != null) { 2875 viewModelStores.add(f.mViewModelStore); 2876 } 2877 } 2878 } 2879 } 2880 if (fragments == null && childFragments == null && viewModelStores == null) { 2881 mSavedNonConfig = null; 2882 } else { 2883 mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments, 2884 viewModelStores); 2885 } 2886 } 2887 2888 void saveFragmentViewState(Fragment f) { 2889 if (f.mInnerView == null) { 2890 return; 2891 } 2892 if (mStateArray == null) { 2893 mStateArray = new SparseArray<Parcelable>(); 2894 } else { 2895 mStateArray.clear(); 2896 } 2897 f.mInnerView.saveHierarchyState(mStateArray); 2898 if (mStateArray.size() > 0) { 2899 f.mSavedViewState = mStateArray; 2900 mStateArray = null; 2901 } 2902 } 2903 2904 Bundle saveFragmentBasicState(Fragment f) { 2905 Bundle result = null; 2906 2907 if (mStateBundle == null) { 2908 mStateBundle = new Bundle(); 2909 } 2910 f.performSaveInstanceState(mStateBundle); 2911 dispatchOnFragmentSaveInstanceState(f, mStateBundle, false); 2912 if (!mStateBundle.isEmpty()) { 2913 result = mStateBundle; 2914 mStateBundle = null; 2915 } 2916 2917 if (f.mView != null) { 2918 saveFragmentViewState(f); 2919 } 2920 if (f.mSavedViewState != null) { 2921 if (result == null) { 2922 result = new Bundle(); 2923 } 2924 result.putSparseParcelableArray( 2925 FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); 2926 } 2927 if (!f.mUserVisibleHint) { 2928 if (result == null) { 2929 result = new Bundle(); 2930 } 2931 // Only add this if it's not the default value 2932 result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint); 2933 } 2934 2935 return result; 2936 } 2937 2938 Parcelable saveAllState() { 2939 // Make sure all pending operations have now been executed to get 2940 // our state update-to-date. 2941 forcePostponedTransactions(); 2942 endAnimatingAwayFragments(); 2943 execPendingActions(); 2944 2945 mStateSaved = true; 2946 mSavedNonConfig = null; 2947 2948 if (mActive == null || mActive.size() <= 0) { 2949 return null; 2950 } 2951 2952 // First collect all active fragments. 2953 int N = mActive.size(); 2954 FragmentState[] active = new FragmentState[N]; 2955 boolean haveFragments = false; 2956 for (int i=0; i<N; i++) { 2957 Fragment f = mActive.valueAt(i); 2958 if (f != null) { 2959 if (f.mIndex < 0) { 2960 throwException(new IllegalStateException( 2961 "Failure saving state: active " + f 2962 + " has cleared index: " + f.mIndex)); 2963 } 2964 2965 haveFragments = true; 2966 2967 FragmentState fs = new FragmentState(f); 2968 active[i] = fs; 2969 2970 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { 2971 fs.mSavedFragmentState = saveFragmentBasicState(f); 2972 2973 if (f.mTarget != null) { 2974 if (f.mTarget.mIndex < 0) { 2975 throwException(new IllegalStateException( 2976 "Failure saving state: " + f 2977 + " has target not in fragment manager: " + f.mTarget)); 2978 } 2979 if (fs.mSavedFragmentState == null) { 2980 fs.mSavedFragmentState = new Bundle(); 2981 } 2982 putFragment(fs.mSavedFragmentState, 2983 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); 2984 if (f.mTargetRequestCode != 0) { 2985 fs.mSavedFragmentState.putInt( 2986 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 2987 f.mTargetRequestCode); 2988 } 2989 } 2990 2991 } else { 2992 fs.mSavedFragmentState = f.mSavedFragmentState; 2993 } 2994 2995 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " 2996 + fs.mSavedFragmentState); 2997 } 2998 } 2999 3000 if (!haveFragments) { 3001 if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); 3002 return null; 3003 } 3004 3005 int[] added = null; 3006 BackStackState[] backStack = null; 3007 3008 // Build list of currently added fragments. 3009 N = mAdded.size(); 3010 if (N > 0) { 3011 added = new int[N]; 3012 for (int i = 0; i < N; i++) { 3013 added[i] = mAdded.get(i).mIndex; 3014 if (added[i] < 0) { 3015 throwException(new IllegalStateException( 3016 "Failure saving state: active " + mAdded.get(i) 3017 + " has cleared index: " + added[i])); 3018 } 3019 if (DEBUG) { 3020 Log.v(TAG, "saveAllState: adding fragment #" + i 3021 + ": " + mAdded.get(i)); 3022 } 3023 } 3024 } 3025 3026 // Now save back stack. 3027 if (mBackStack != null) { 3028 N = mBackStack.size(); 3029 if (N > 0) { 3030 backStack = new BackStackState[N]; 3031 for (int i=0; i<N; i++) { 3032 backStack[i] = new BackStackState(mBackStack.get(i)); 3033 if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i 3034 + ": " + mBackStack.get(i)); 3035 } 3036 } 3037 } 3038 3039 FragmentManagerState fms = new FragmentManagerState(); 3040 fms.mActive = active; 3041 fms.mAdded = added; 3042 fms.mBackStack = backStack; 3043 if (mPrimaryNav != null) { 3044 fms.mPrimaryNavActiveIndex = mPrimaryNav.mIndex; 3045 } 3046 fms.mNextFragmentIndex = mNextFragmentIndex; 3047 saveNonConfig(); 3048 return fms; 3049 } 3050 3051 void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) { 3052 // If there is no saved state at all, then there can not be 3053 // any nonConfig fragments either, so that is that. 3054 if (state == null) return; 3055 FragmentManagerState fms = (FragmentManagerState)state; 3056 if (fms.mActive == null) return; 3057 3058 List<FragmentManagerNonConfig> childNonConfigs = null; 3059 List<ViewModelStore> viewModelStores = null; 3060 3061 // First re-attach any non-config instances we are retaining back 3062 // to their saved state, so we don't try to instantiate them again. 3063 if (nonConfig != null) { 3064 List<Fragment> nonConfigFragments = nonConfig.getFragments(); 3065 childNonConfigs = nonConfig.getChildNonConfigs(); 3066 viewModelStores = nonConfig.getViewModelStores(); 3067 final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0; 3068 for (int i = 0; i < count; i++) { 3069 Fragment f = nonConfigFragments.get(i); 3070 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f); 3071 int index = 0; // index into fms.mActive 3072 while (index < fms.mActive.length && fms.mActive[index].mIndex != f.mIndex) { 3073 index++; 3074 } 3075 if (index == fms.mActive.length) { 3076 throwException(new IllegalStateException("Could not find active fragment " 3077 + "with index " + f.mIndex)); 3078 } 3079 FragmentState fs = fms.mActive[index]; 3080 fs.mInstance = f; 3081 f.mSavedViewState = null; 3082 f.mBackStackNesting = 0; 3083 f.mInLayout = false; 3084 f.mAdded = false; 3085 f.mTarget = null; 3086 if (fs.mSavedFragmentState != null) { 3087 fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader()); 3088 f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( 3089 FragmentManagerImpl.VIEW_STATE_TAG); 3090 f.mSavedFragmentState = fs.mSavedFragmentState; 3091 } 3092 } 3093 } 3094 3095 // Build the full list of active fragments, instantiating them from 3096 // their saved state. 3097 mActive = new SparseArray<>(fms.mActive.length); 3098 for (int i=0; i<fms.mActive.length; i++) { 3099 FragmentState fs = fms.mActive[i]; 3100 if (fs != null) { 3101 FragmentManagerNonConfig childNonConfig = null; 3102 if (childNonConfigs != null && i < childNonConfigs.size()) { 3103 childNonConfig = childNonConfigs.get(i); 3104 } 3105 ViewModelStore viewModelStore = null; 3106 if (viewModelStores != null && i < viewModelStores.size()) { 3107 viewModelStore = viewModelStores.get(i); 3108 } 3109 Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig, 3110 viewModelStore); 3111 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f); 3112 mActive.put(f.mIndex, f); 3113 // Now that the fragment is instantiated (or came from being 3114 // retained above), clear mInstance in case we end up re-restoring 3115 // from this FragmentState again. 3116 fs.mInstance = null; 3117 } 3118 } 3119 3120 // Update the target of all retained fragments. 3121 if (nonConfig != null) { 3122 List<Fragment> nonConfigFragments = nonConfig.getFragments(); 3123 final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0; 3124 for (int i = 0; i < count; i++) { 3125 Fragment f = nonConfigFragments.get(i); 3126 if (f.mTargetIndex >= 0) { 3127 f.mTarget = mActive.get(f.mTargetIndex); 3128 if (f.mTarget == null) { 3129 Log.w(TAG, "Re-attaching retained fragment " + f 3130 + " target no longer exists: " + f.mTargetIndex); 3131 } 3132 } 3133 } 3134 } 3135 3136 // Build the list of currently added fragments. 3137 mAdded.clear(); 3138 if (fms.mAdded != null) { 3139 for (int i=0; i<fms.mAdded.length; i++) { 3140 Fragment f = mActive.get(fms.mAdded[i]); 3141 if (f == null) { 3142 throwException(new IllegalStateException( 3143 "No instantiated fragment for index #" + fms.mAdded[i])); 3144 } 3145 f.mAdded = true; 3146 if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f); 3147 if (mAdded.contains(f)) { 3148 throw new IllegalStateException("Already added!"); 3149 } 3150 synchronized (mAdded) { 3151 mAdded.add(f); 3152 } 3153 } 3154 } 3155 3156 // Build the back stack. 3157 if (fms.mBackStack != null) { 3158 mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); 3159 for (int i=0; i<fms.mBackStack.length; i++) { 3160 BackStackRecord bse = fms.mBackStack[i].instantiate(this); 3161 if (DEBUG) { 3162 Log.v(TAG, "restoreAllState: back stack #" + i 3163 + " (index " + bse.mIndex + "): " + bse); 3164 LogWriter logw = new LogWriter(TAG); 3165 PrintWriter pw = new PrintWriter(logw); 3166 bse.dump(" ", pw, false); 3167 pw.close(); 3168 } 3169 mBackStack.add(bse); 3170 if (bse.mIndex >= 0) { 3171 setBackStackIndex(bse.mIndex, bse); 3172 } 3173 } 3174 } else { 3175 mBackStack = null; 3176 } 3177 3178 if (fms.mPrimaryNavActiveIndex >= 0) { 3179 mPrimaryNav = mActive.get(fms.mPrimaryNavActiveIndex); 3180 } 3181 this.mNextFragmentIndex = fms.mNextFragmentIndex; 3182 } 3183 3184 /** 3185 * To prevent list modification errors, mActive sets values to null instead of 3186 * removing them when the Fragment becomes inactive. This cleans up the list at the 3187 * end of executing the transactions. 3188 */ 3189 private void burpActive() { 3190 if (mActive != null) { 3191 for (int i = mActive.size() - 1; i >= 0; i--) { 3192 if (mActive.valueAt(i) == null) { 3193 mActive.delete(mActive.keyAt(i)); 3194 } 3195 } 3196 } 3197 } 3198 3199 public void attachController(FragmentHostCallback host, 3200 FragmentContainer container, Fragment parent) { 3201 if (mHost != null) throw new IllegalStateException("Already attached"); 3202 mHost = host; 3203 mContainer = container; 3204 mParent = parent; 3205 } 3206 3207 public void noteStateNotSaved() { 3208 mSavedNonConfig = null; 3209 mStateSaved = false; 3210 mStopped = false; 3211 final int addedCount = mAdded.size(); 3212 for (int i = 0; i < addedCount; i++) { 3213 Fragment fragment = mAdded.get(i); 3214 if (fragment != null) { 3215 fragment.noteStateNotSaved(); 3216 } 3217 } 3218 } 3219 3220 public void dispatchCreate() { 3221 mStateSaved = false; 3222 mStopped = false; 3223 dispatchStateChange(Fragment.CREATED); 3224 } 3225 3226 public void dispatchActivityCreated() { 3227 mStateSaved = false; 3228 mStopped = false; 3229 dispatchStateChange(Fragment.ACTIVITY_CREATED); 3230 } 3231 3232 public void dispatchStart() { 3233 mStateSaved = false; 3234 mStopped = false; 3235 dispatchStateChange(Fragment.STARTED); 3236 } 3237 3238 public void dispatchResume() { 3239 mStateSaved = false; 3240 mStopped = false; 3241 dispatchStateChange(Fragment.RESUMED); 3242 } 3243 3244 public void dispatchPause() { 3245 dispatchStateChange(Fragment.STARTED); 3246 } 3247 3248 public void dispatchStop() { 3249 mStopped = true; 3250 dispatchStateChange(Fragment.STOPPED); 3251 } 3252 3253 public void dispatchReallyStop() { 3254 dispatchStateChange(Fragment.ACTIVITY_CREATED); 3255 } 3256 3257 public void dispatchDestroyView() { 3258 dispatchStateChange(Fragment.CREATED); 3259 } 3260 3261 public void dispatchDestroy() { 3262 mDestroyed = true; 3263 execPendingActions(); 3264 dispatchStateChange(Fragment.INITIALIZING); 3265 mHost = null; 3266 mContainer = null; 3267 mParent = null; 3268 } 3269 3270 private void dispatchStateChange(int nextState) { 3271 try { 3272 mExecutingActions = true; 3273 moveToState(nextState, false); 3274 } finally { 3275 mExecutingActions = false; 3276 } 3277 execPendingActions(); 3278 } 3279 3280 public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) { 3281 for (int i = mAdded.size() - 1; i >= 0; --i) { 3282 final Fragment f = mAdded.get(i); 3283 if (f != null) { 3284 f.performMultiWindowModeChanged(isInMultiWindowMode); 3285 } 3286 } 3287 } 3288 3289 public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) { 3290 for (int i = mAdded.size() - 1; i >= 0; --i) { 3291 final Fragment f = mAdded.get(i); 3292 if (f != null) { 3293 f.performPictureInPictureModeChanged(isInPictureInPictureMode); 3294 } 3295 } 3296 } 3297 3298 public void dispatchConfigurationChanged(Configuration newConfig) { 3299 for (int i = 0; i < mAdded.size(); i++) { 3300 Fragment f = mAdded.get(i); 3301 if (f != null) { 3302 f.performConfigurationChanged(newConfig); 3303 } 3304 } 3305 } 3306 3307 public void dispatchLowMemory() { 3308 for (int i = 0; i < mAdded.size(); i++) { 3309 Fragment f = mAdded.get(i); 3310 if (f != null) { 3311 f.performLowMemory(); 3312 } 3313 } 3314 } 3315 3316 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 3317 if (mCurState < Fragment.CREATED) { 3318 return false; 3319 } 3320 boolean show = false; 3321 ArrayList<Fragment> newMenus = null; 3322 for (int i = 0; i < mAdded.size(); i++) { 3323 Fragment f = mAdded.get(i); 3324 if (f != null) { 3325 if (f.performCreateOptionsMenu(menu, inflater)) { 3326 show = true; 3327 if (newMenus == null) { 3328 newMenus = new ArrayList<Fragment>(); 3329 } 3330 newMenus.add(f); 3331 } 3332 } 3333 } 3334 3335 if (mCreatedMenus != null) { 3336 for (int i=0; i<mCreatedMenus.size(); i++) { 3337 Fragment f = mCreatedMenus.get(i); 3338 if (newMenus == null || !newMenus.contains(f)) { 3339 f.onDestroyOptionsMenu(); 3340 } 3341 } 3342 } 3343 3344 mCreatedMenus = newMenus; 3345 3346 return show; 3347 } 3348 3349 public boolean dispatchPrepareOptionsMenu(Menu menu) { 3350 if (mCurState < Fragment.CREATED) { 3351 return false; 3352 } 3353 boolean show = false; 3354 for (int i = 0; i < mAdded.size(); i++) { 3355 Fragment f = mAdded.get(i); 3356 if (f != null) { 3357 if (f.performPrepareOptionsMenu(menu)) { 3358 show = true; 3359 } 3360 } 3361 } 3362 return show; 3363 } 3364 3365 public boolean dispatchOptionsItemSelected(MenuItem item) { 3366 if (mCurState < Fragment.CREATED) { 3367 return false; 3368 } 3369 for (int i = 0; i < mAdded.size(); i++) { 3370 Fragment f = mAdded.get(i); 3371 if (f != null) { 3372 if (f.performOptionsItemSelected(item)) { 3373 return true; 3374 } 3375 } 3376 } 3377 return false; 3378 } 3379 3380 public boolean dispatchContextItemSelected(MenuItem item) { 3381 if (mCurState < Fragment.CREATED) { 3382 return false; 3383 } 3384 for (int i = 0; i < mAdded.size(); i++) { 3385 Fragment f = mAdded.get(i); 3386 if (f != null) { 3387 if (f.performContextItemSelected(item)) { 3388 return true; 3389 } 3390 } 3391 } 3392 return false; 3393 } 3394 3395 public void dispatchOptionsMenuClosed(Menu menu) { 3396 if (mCurState < Fragment.CREATED) { 3397 return; 3398 } 3399 for (int i = 0; i < mAdded.size(); i++) { 3400 Fragment f = mAdded.get(i); 3401 if (f != null) { 3402 f.performOptionsMenuClosed(menu); 3403 } 3404 } 3405 } 3406 3407 @SuppressWarnings("ReferenceEquality") 3408 public void setPrimaryNavigationFragment(Fragment f) { 3409 if (f != null && (mActive.get(f.mIndex) != f 3410 || (f.mHost != null && f.getFragmentManager() != this))) { 3411 throw new IllegalArgumentException("Fragment " + f 3412 + " is not an active fragment of FragmentManager " + this); 3413 } 3414 mPrimaryNav = f; 3415 } 3416 3417 @Override 3418 @Nullable 3419 public Fragment getPrimaryNavigationFragment() { 3420 return mPrimaryNav; 3421 } 3422 3423 @Override 3424 public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb, 3425 boolean recursive) { 3426 mLifecycleCallbacks.add(new FragmentLifecycleCallbacksHolder(cb, recursive)); 3427 } 3428 3429 @Override 3430 public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) { 3431 synchronized (mLifecycleCallbacks) { 3432 for (int i = 0, N = mLifecycleCallbacks.size(); i < N; i++) { 3433 if (mLifecycleCallbacks.get(i).mCallback == cb) { 3434 mLifecycleCallbacks.remove(i); 3435 break; 3436 } 3437 } 3438 } 3439 } 3440 3441 void dispatchOnFragmentPreAttached(@NonNull Fragment f, @NonNull Context context, 3442 boolean onlyRecursive) { 3443 if (mParent != null) { 3444 FragmentManager parentManager = mParent.getFragmentManager(); 3445 if (parentManager instanceof FragmentManagerImpl) { 3446 ((FragmentManagerImpl) parentManager) 3447 .dispatchOnFragmentPreAttached(f, context, true); 3448 } 3449 } 3450 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3451 if (!onlyRecursive || holder.mRecursive) { 3452 holder.mCallback.onFragmentPreAttached(this, f, context); 3453 } 3454 } 3455 } 3456 3457 void dispatchOnFragmentAttached(@NonNull Fragment f, @NonNull Context context, 3458 boolean onlyRecursive) { 3459 if (mParent != null) { 3460 FragmentManager parentManager = mParent.getFragmentManager(); 3461 if (parentManager instanceof FragmentManagerImpl) { 3462 ((FragmentManagerImpl) parentManager) 3463 .dispatchOnFragmentAttached(f, context, true); 3464 } 3465 } 3466 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3467 if (!onlyRecursive || holder.mRecursive) { 3468 holder.mCallback.onFragmentAttached(this, f, context); 3469 } 3470 } 3471 } 3472 3473 void dispatchOnFragmentPreCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState, 3474 boolean onlyRecursive) { 3475 if (mParent != null) { 3476 FragmentManager parentManager = mParent.getFragmentManager(); 3477 if (parentManager instanceof FragmentManagerImpl) { 3478 ((FragmentManagerImpl) parentManager) 3479 .dispatchOnFragmentPreCreated(f, savedInstanceState, true); 3480 } 3481 } 3482 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3483 if (!onlyRecursive || holder.mRecursive) { 3484 holder.mCallback.onFragmentPreCreated(this, f, savedInstanceState); 3485 } 3486 } 3487 } 3488 3489 void dispatchOnFragmentCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState, 3490 boolean onlyRecursive) { 3491 if (mParent != null) { 3492 FragmentManager parentManager = mParent.getFragmentManager(); 3493 if (parentManager instanceof FragmentManagerImpl) { 3494 ((FragmentManagerImpl) parentManager) 3495 .dispatchOnFragmentCreated(f, savedInstanceState, true); 3496 } 3497 } 3498 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3499 if (!onlyRecursive || holder.mRecursive) { 3500 holder.mCallback.onFragmentCreated(this, f, savedInstanceState); 3501 } 3502 } 3503 } 3504 3505 void dispatchOnFragmentActivityCreated(@NonNull Fragment f, @Nullable Bundle savedInstanceState, 3506 boolean onlyRecursive) { 3507 if (mParent != null) { 3508 FragmentManager parentManager = mParent.getFragmentManager(); 3509 if (parentManager instanceof FragmentManagerImpl) { 3510 ((FragmentManagerImpl) parentManager) 3511 .dispatchOnFragmentActivityCreated(f, savedInstanceState, true); 3512 } 3513 } 3514 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3515 if (!onlyRecursive || holder.mRecursive) { 3516 holder.mCallback.onFragmentActivityCreated(this, f, savedInstanceState); 3517 } 3518 } 3519 } 3520 3521 void dispatchOnFragmentViewCreated(@NonNull Fragment f, @NonNull View v, 3522 @Nullable Bundle savedInstanceState, boolean onlyRecursive) { 3523 if (mParent != null) { 3524 FragmentManager parentManager = mParent.getFragmentManager(); 3525 if (parentManager instanceof FragmentManagerImpl) { 3526 ((FragmentManagerImpl) parentManager) 3527 .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true); 3528 } 3529 } 3530 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3531 if (!onlyRecursive || holder.mRecursive) { 3532 holder.mCallback.onFragmentViewCreated(this, f, v, savedInstanceState); 3533 } 3534 } 3535 } 3536 3537 void dispatchOnFragmentStarted(@NonNull Fragment f, boolean onlyRecursive) { 3538 if (mParent != null) { 3539 FragmentManager parentManager = mParent.getFragmentManager(); 3540 if (parentManager instanceof FragmentManagerImpl) { 3541 ((FragmentManagerImpl) parentManager) 3542 .dispatchOnFragmentStarted(f, true); 3543 } 3544 } 3545 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3546 if (!onlyRecursive || holder.mRecursive) { 3547 holder.mCallback.onFragmentStarted(this, f); 3548 } 3549 } 3550 } 3551 3552 void dispatchOnFragmentResumed(@NonNull Fragment f, boolean onlyRecursive) { 3553 if (mParent != null) { 3554 FragmentManager parentManager = mParent.getFragmentManager(); 3555 if (parentManager instanceof FragmentManagerImpl) { 3556 ((FragmentManagerImpl) parentManager) 3557 .dispatchOnFragmentResumed(f, true); 3558 } 3559 } 3560 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3561 if (!onlyRecursive || holder.mRecursive) { 3562 holder.mCallback.onFragmentResumed(this, f); 3563 } 3564 } 3565 } 3566 3567 void dispatchOnFragmentPaused(@NonNull Fragment f, boolean onlyRecursive) { 3568 if (mParent != null) { 3569 FragmentManager parentManager = mParent.getFragmentManager(); 3570 if (parentManager instanceof FragmentManagerImpl) { 3571 ((FragmentManagerImpl) parentManager) 3572 .dispatchOnFragmentPaused(f, true); 3573 } 3574 } 3575 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3576 if (!onlyRecursive || holder.mRecursive) { 3577 holder.mCallback.onFragmentPaused(this, f); 3578 } 3579 } 3580 } 3581 3582 void dispatchOnFragmentStopped(@NonNull Fragment f, boolean onlyRecursive) { 3583 if (mParent != null) { 3584 FragmentManager parentManager = mParent.getFragmentManager(); 3585 if (parentManager instanceof FragmentManagerImpl) { 3586 ((FragmentManagerImpl) parentManager) 3587 .dispatchOnFragmentStopped(f, true); 3588 } 3589 } 3590 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3591 if (!onlyRecursive || holder.mRecursive) { 3592 holder.mCallback.onFragmentStopped(this, f); 3593 } 3594 } 3595 } 3596 3597 void dispatchOnFragmentSaveInstanceState(@NonNull Fragment f, @NonNull Bundle outState, 3598 boolean onlyRecursive) { 3599 if (mParent != null) { 3600 FragmentManager parentManager = mParent.getFragmentManager(); 3601 if (parentManager instanceof FragmentManagerImpl) { 3602 ((FragmentManagerImpl) parentManager) 3603 .dispatchOnFragmentSaveInstanceState(f, outState, true); 3604 } 3605 } 3606 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3607 if (!onlyRecursive || holder.mRecursive) { 3608 holder.mCallback.onFragmentSaveInstanceState(this, f, outState); 3609 } 3610 } 3611 } 3612 3613 void dispatchOnFragmentViewDestroyed(@NonNull Fragment f, boolean onlyRecursive) { 3614 if (mParent != null) { 3615 FragmentManager parentManager = mParent.getFragmentManager(); 3616 if (parentManager instanceof FragmentManagerImpl) { 3617 ((FragmentManagerImpl) parentManager) 3618 .dispatchOnFragmentViewDestroyed(f, true); 3619 } 3620 } 3621 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3622 if (!onlyRecursive || holder.mRecursive) { 3623 holder.mCallback.onFragmentViewDestroyed(this, f); 3624 } 3625 } 3626 } 3627 3628 void dispatchOnFragmentDestroyed(@NonNull Fragment f, boolean onlyRecursive) { 3629 if (mParent != null) { 3630 FragmentManager parentManager = mParent.getFragmentManager(); 3631 if (parentManager instanceof FragmentManagerImpl) { 3632 ((FragmentManagerImpl) parentManager) 3633 .dispatchOnFragmentDestroyed(f, true); 3634 } 3635 } 3636 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3637 if (!onlyRecursive || holder.mRecursive) { 3638 holder.mCallback.onFragmentDestroyed(this, f); 3639 } 3640 } 3641 } 3642 3643 void dispatchOnFragmentDetached(@NonNull Fragment f, boolean onlyRecursive) { 3644 if (mParent != null) { 3645 FragmentManager parentManager = mParent.getFragmentManager(); 3646 if (parentManager instanceof FragmentManagerImpl) { 3647 ((FragmentManagerImpl) parentManager) 3648 .dispatchOnFragmentDetached(f, true); 3649 } 3650 } 3651 for (FragmentLifecycleCallbacksHolder holder : mLifecycleCallbacks) { 3652 if (!onlyRecursive || holder.mRecursive) { 3653 holder.mCallback.onFragmentDetached(this, f); 3654 } 3655 } 3656 } 3657 3658 public static int reverseTransit(int transit) { 3659 int rev = 0; 3660 switch (transit) { 3661 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 3662 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; 3663 break; 3664 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 3665 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; 3666 break; 3667 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 3668 rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE; 3669 break; 3670 } 3671 return rev; 3672 3673 } 3674 3675 public static final int ANIM_STYLE_OPEN_ENTER = 1; 3676 public static final int ANIM_STYLE_OPEN_EXIT = 2; 3677 public static final int ANIM_STYLE_CLOSE_ENTER = 3; 3678 public static final int ANIM_STYLE_CLOSE_EXIT = 4; 3679 public static final int ANIM_STYLE_FADE_ENTER = 5; 3680 public static final int ANIM_STYLE_FADE_EXIT = 6; 3681 3682 public static int transitToStyleIndex(int transit, boolean enter) { 3683 int animAttr = -1; 3684 switch (transit) { 3685 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 3686 animAttr = enter ? ANIM_STYLE_OPEN_ENTER : ANIM_STYLE_OPEN_EXIT; 3687 break; 3688 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 3689 animAttr = enter ? ANIM_STYLE_CLOSE_ENTER : ANIM_STYLE_CLOSE_EXIT; 3690 break; 3691 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 3692 animAttr = enter ? ANIM_STYLE_FADE_ENTER : ANIM_STYLE_FADE_EXIT; 3693 break; 3694 } 3695 return animAttr; 3696 } 3697 3698 @Override 3699 public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { 3700 if (!"fragment".equals(name)) { 3701 return null; 3702 } 3703 3704 String fname = attrs.getAttributeValue(null, "class"); 3705 TypedArray a = context.obtainStyledAttributes(attrs, FragmentTag.Fragment); 3706 if (fname == null) { 3707 fname = a.getString(FragmentTag.Fragment_name); 3708 } 3709 int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID); 3710 String tag = a.getString(FragmentTag.Fragment_tag); 3711 a.recycle(); 3712 3713 if (!Fragment.isSupportFragmentClass(mHost.getContext(), fname)) { 3714 // Invalid support lib fragment; let the device's framework handle it. 3715 // This will allow android.app.Fragments to do the right thing. 3716 return null; 3717 } 3718 3719 int containerId = parent != null ? parent.getId() : 0; 3720 if (containerId == View.NO_ID && id == View.NO_ID && tag == null) { 3721 throw new IllegalArgumentException(attrs.getPositionDescription() 3722 + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname); 3723 } 3724 3725 // If we restored from a previous state, we may already have 3726 // instantiated this fragment from the state and should use 3727 // that instance instead of making a new one. 3728 Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null; 3729 if (fragment == null && tag != null) { 3730 fragment = findFragmentByTag(tag); 3731 } 3732 if (fragment == null && containerId != View.NO_ID) { 3733 fragment = findFragmentById(containerId); 3734 } 3735 3736 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x" 3737 + Integer.toHexString(id) + " fname=" + fname 3738 + " existing=" + fragment); 3739 if (fragment == null) { 3740 fragment = mContainer.instantiate(context, fname, null); 3741 fragment.mFromLayout = true; 3742 fragment.mFragmentId = id != 0 ? id : containerId; 3743 fragment.mContainerId = containerId; 3744 fragment.mTag = tag; 3745 fragment.mInLayout = true; 3746 fragment.mFragmentManager = this; 3747 fragment.mHost = mHost; 3748 fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState); 3749 addFragment(fragment, true); 3750 3751 } else if (fragment.mInLayout) { 3752 // A fragment already exists and it is not one we restored from 3753 // previous state. 3754 throw new IllegalArgumentException(attrs.getPositionDescription() 3755 + ": Duplicate id 0x" + Integer.toHexString(id) 3756 + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId) 3757 + " with another fragment for " + fname); 3758 } else { 3759 // This fragment was retained from a previous instance; get it 3760 // going now. 3761 fragment.mInLayout = true; 3762 fragment.mHost = mHost; 3763 // If this fragment is newly instantiated (either right now, or 3764 // from last saved state), then give it the attributes to 3765 // initialize itself. 3766 if (!fragment.mRetaining) { 3767 fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState); 3768 } 3769 } 3770 3771 // If we haven't finished entering the CREATED state ourselves yet, 3772 // push the inflated child fragment along. This will ensureInflatedFragmentView 3773 // at the right phase of the lifecycle so that we will have mView populated 3774 // for compliant fragments below. 3775 if (mCurState < Fragment.CREATED && fragment.mFromLayout) { 3776 moveToState(fragment, Fragment.CREATED, 0, 0, false); 3777 } else { 3778 moveToState(fragment); 3779 } 3780 3781 if (fragment.mView == null) { 3782 throw new IllegalStateException("Fragment " + fname 3783 + " did not create a view."); 3784 } 3785 if (id != 0) { 3786 fragment.mView.setId(id); 3787 } 3788 if (fragment.mView.getTag() == null) { 3789 fragment.mView.setTag(tag); 3790 } 3791 return fragment.mView; 3792 } 3793 3794 @Override 3795 public View onCreateView(String name, Context context, AttributeSet attrs) { 3796 return onCreateView(null, name, context, attrs); 3797 } 3798 3799 LayoutInflater.Factory2 getLayoutInflaterFactory() { 3800 return this; 3801 } 3802 3803 static class FragmentTag { 3804 public static final int[] Fragment = { 3805 0x01010003, 0x010100d0, 0x010100d1 3806 }; 3807 public static final int Fragment_id = 1; 3808 public static final int Fragment_name = 0; 3809 public static final int Fragment_tag = 2; 3810 } 3811 3812 /** 3813 * An add or pop transaction to be scheduled for the UI thread. 3814 */ 3815 interface OpGenerator { 3816 /** 3817 * Generate transactions to add to {@code records} and whether or not the transaction is 3818 * an add or pop to {@code isRecordPop}. 3819 * 3820 * records and isRecordPop must be added equally so that each transaction in records 3821 * matches the boolean for whether or not it is a pop in isRecordPop. 3822 * 3823 * @param records A list to add transactions to. 3824 * @param isRecordPop A list to add whether or not the transactions added to records is 3825 * a pop transaction. 3826 * @return true if something was added or false otherwise. 3827 */ 3828 boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop); 3829 } 3830 3831 /** 3832 * A pop operation OpGenerator. This will be run on the UI thread and will generate the 3833 * transactions that will be popped if anything can be popped. 3834 */ 3835 private class PopBackStackState implements OpGenerator { 3836 final String mName; 3837 final int mId; 3838 final int mFlags; 3839 3840 PopBackStackState(String name, int id, int flags) { 3841 mName = name; 3842 mId = id; 3843 mFlags = flags; 3844 } 3845 3846 @Override 3847 public boolean generateOps(ArrayList<BackStackRecord> records, 3848 ArrayList<Boolean> isRecordPop) { 3849 if (mPrimaryNav != null // We have a primary nav fragment 3850 && mId < 0 // No valid id (since they're local) 3851 && mName == null) { // no name to pop to (since they're local) 3852 final FragmentManager childManager = mPrimaryNav.peekChildFragmentManager(); 3853 if (childManager != null && childManager.popBackStackImmediate()) { 3854 // We didn't add any operations for this FragmentManager even though 3855 // a child did do work. 3856 return false; 3857 } 3858 } 3859 return popBackStackState(records, isRecordPop, mName, mId, mFlags); 3860 } 3861 } 3862 3863 /** 3864 * A listener for a postponed transaction. This waits until 3865 * {@link Fragment#startPostponedEnterTransition()} is called or a transaction is started 3866 * that interacts with this one, based on interactions with the fragment container. 3867 */ 3868 static class StartEnterTransitionListener 3869 implements Fragment.OnStartEnterTransitionListener { 3870 private final boolean mIsBack; 3871 private final BackStackRecord mRecord; 3872 private int mNumPostponed; 3873 3874 StartEnterTransitionListener(BackStackRecord record, boolean isBack) { 3875 mIsBack = isBack; 3876 mRecord = record; 3877 } 3878 3879 /** 3880 * Called from {@link Fragment#startPostponedEnterTransition()}, this decreases the 3881 * number of Fragments that are postponed. This may cause the transaction to schedule 3882 * to finish running and run transitions and animations. 3883 */ 3884 @Override 3885 public void onStartEnterTransition() { 3886 mNumPostponed--; 3887 if (mNumPostponed != 0) { 3888 return; 3889 } 3890 mRecord.mManager.scheduleCommit(); 3891 } 3892 3893 /** 3894 * Called from {@link Fragment# 3895 * setOnStartEnterTransitionListener(Fragment.OnStartEnterTransitionListener)}, this 3896 * increases the number of fragments that are postponed as part of this transaction. 3897 */ 3898 @Override 3899 public void startListening() { 3900 mNumPostponed++; 3901 } 3902 3903 /** 3904 * @return true if there are no more postponed fragments as part of the transaction. 3905 */ 3906 public boolean isReady() { 3907 return mNumPostponed == 0; 3908 } 3909 3910 /** 3911 * Completes the transaction and start the animations and transitions. This may skip 3912 * the transitions if this is called before all fragments have called 3913 * {@link Fragment#startPostponedEnterTransition()}. 3914 */ 3915 public void completeTransaction() { 3916 final boolean canceled; 3917 canceled = mNumPostponed > 0; 3918 FragmentManagerImpl manager = mRecord.mManager; 3919 final int numAdded = manager.mAdded.size(); 3920 for (int i = 0; i < numAdded; i++) { 3921 final Fragment fragment = manager.mAdded.get(i); 3922 fragment.setOnStartEnterTransitionListener(null); 3923 if (canceled && fragment.isPostponed()) { 3924 fragment.startPostponedEnterTransition(); 3925 } 3926 } 3927 mRecord.mManager.completeExecute(mRecord, mIsBack, !canceled, true); 3928 } 3929 3930 /** 3931 * Cancels this transaction instead of completing it. That means that the state isn't 3932 * changed, so the pop results in no change to the state. 3933 */ 3934 public void cancelTransaction() { 3935 mRecord.mManager.completeExecute(mRecord, mIsBack, false, false); 3936 } 3937 } 3938 3939 /** 3940 * Contains either an animator or animation. One of these should be null. 3941 */ 3942 private static class AnimationOrAnimator { 3943 public final Animation animation; 3944 public final Animator animator; 3945 3946 private AnimationOrAnimator(Animation animation) { 3947 this.animation = animation; 3948 this.animator = null; 3949 if (animation == null) { 3950 throw new IllegalStateException("Animation cannot be null"); 3951 } 3952 } 3953 3954 private AnimationOrAnimator(Animator animator) { 3955 this.animation = null; 3956 this.animator = animator; 3957 if (animator == null) { 3958 throw new IllegalStateException("Animator cannot be null"); 3959 } 3960 } 3961 } 3962 3963 /** 3964 * Wrap an AnimationListener that can be null. This allows us to chain animation listeners. 3965 */ 3966 private static class AnimationListenerWrapper implements AnimationListener { 3967 private final AnimationListener mWrapped; 3968 3969 private AnimationListenerWrapper(AnimationListener wrapped) { 3970 mWrapped = wrapped; 3971 } 3972 3973 @CallSuper 3974 @Override 3975 public void onAnimationStart(Animation animation) { 3976 if (mWrapped != null) { 3977 mWrapped.onAnimationStart(animation); 3978 } 3979 } 3980 3981 @CallSuper 3982 @Override 3983 public void onAnimationEnd(Animation animation) { 3984 if (mWrapped != null) { 3985 mWrapped.onAnimationEnd(animation); 3986 } 3987 } 3988 3989 @CallSuper 3990 @Override 3991 public void onAnimationRepeat(Animation animation) { 3992 if (mWrapped != null) { 3993 mWrapped.onAnimationRepeat(animation); 3994 } 3995 } 3996 } 3997 3998 /** 3999 * Reset the layer type to LAYER_TYPE_NONE at the end of an animation. 4000 */ 4001 private static class AnimateOnHWLayerIfNeededListener extends AnimationListenerWrapper { 4002 View mView; 4003 4004 AnimateOnHWLayerIfNeededListener(final View v, AnimationListener listener) { 4005 super(listener); 4006 mView = v; 4007 } 4008 4009 @Override 4010 @CallSuper 4011 public void onAnimationEnd(Animation animation) { 4012 // If we're attached to a window, assume we're in the normal performTraversals 4013 // drawing path for Animations running. It's not safe to change the layer type 4014 // during drawing, so post it to the View to run later. If we're not attached 4015 // or we're running on N and above, post it to the view. If we're not on N and 4016 // not attached, do it right now since existing platform versions don't run the 4017 // hwui renderer for detached views off the UI thread making changing layer type 4018 // safe, but posting may not be. 4019 // Prior to N posting to a detached view from a non-Looper thread could cause 4020 // leaks, since the thread-local run queue on a non-Looper thread would never 4021 // be flushed. 4022 if (ViewCompat.isAttachedToWindow(mView) || Build.VERSION.SDK_INT >= 24) { 4023 mView.post(new Runnable() { 4024 @Override 4025 public void run() { 4026 mView.setLayerType(View.LAYER_TYPE_NONE, null); 4027 } 4028 }); 4029 } else { 4030 mView.setLayerType(View.LAYER_TYPE_NONE, null); 4031 } 4032 super.onAnimationEnd(animation); 4033 } 4034 } 4035 4036 /** 4037 * Set the layer type to LAYER_TYPE_HARDWARE while an animator is running. 4038 */ 4039 private static class AnimatorOnHWLayerIfNeededListener extends AnimatorListenerAdapter { 4040 View mView; 4041 4042 AnimatorOnHWLayerIfNeededListener(final View v) { 4043 mView = v; 4044 } 4045 4046 @Override 4047 public void onAnimationStart(Animator animation) { 4048 mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); 4049 } 4050 4051 @Override 4052 public void onAnimationEnd(Animator animation) { 4053 mView.setLayerType(View.LAYER_TYPE_NONE, null); 4054 animation.removeListener(this); 4055 } 4056 } 4057 4058 /** 4059 * We must call endViewTransition() before the animation ends or else the parent doesn't 4060 * get nulled out. We use both startViewTransition() and startAnimation() to solve a problem 4061 * with Views remaining in the hierarchy as disappearing children after the view has been 4062 * removed in some edge cases. 4063 */ 4064 private static class EndViewTransitionAnimator extends AnimationSet implements Runnable { 4065 private final ViewGroup mParent; 4066 private final View mChild; 4067 private boolean mEnded; 4068 private boolean mTransitionEnded; 4069 4070 EndViewTransitionAnimator(@NonNull Animation animation, 4071 @NonNull ViewGroup parent, @NonNull View child) { 4072 super(false); 4073 mParent = parent; 4074 mChild = child; 4075 addAnimation(animation); 4076 } 4077 4078 @Override 4079 public boolean getTransformation(long currentTime, Transformation t) { 4080 if (mEnded) { 4081 return !mTransitionEnded; 4082 } 4083 boolean more = super.getTransformation(currentTime, t); 4084 if (!more) { 4085 mEnded = true; 4086 OneShotPreDrawListener.add(mParent, this); 4087 } 4088 return true; 4089 } 4090 4091 @Override 4092 public boolean getTransformation(long currentTime, Transformation outTransformation, 4093 float scale) { 4094 if (mEnded) { 4095 return !mTransitionEnded; 4096 } 4097 boolean more = super.getTransformation(currentTime, outTransformation, scale); 4098 if (!more) { 4099 mEnded = true; 4100 OneShotPreDrawListener.add(mParent, this); 4101 } 4102 return true; 4103 } 4104 4105 @Override 4106 public void run() { 4107 mParent.endViewTransition(mChild); 4108 mTransitionEnded = true; 4109 } 4110 } 4111} 4112