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