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