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.content.res.Configuration; 23 import android.content.res.TypedArray; 24 import android.os.Bundle; 25 import android.os.Debug; 26 import android.os.Handler; 27 import android.os.Looper; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.util.DebugUtils; 31 import android.util.Log; 32 import android.util.LogWriter; 33 import android.util.SparseArray; 34 import android.view.Menu; 35 import android.view.MenuInflater; 36 import android.view.MenuItem; 37 import android.view.View; 38 import android.view.ViewGroup; 39 40 import java.io.FileDescriptor; 41 import java.io.PrintWriter; 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 45 /** 46 * Interface for interacting with {@link Fragment} objects inside of an 47 * {@link Activity} 48 * 49 * <div class="special reference"> 50 * <h3>Developer Guides</h3> 51 * <p>For more information about using fragments, read the 52 * <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> 53 * </div> 54 * 55 * While the FragmentManager API was introduced in 56 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API 57 * at is also available for use on older platforms through 58 * {@link android.support.v4.app.FragmentActivity}. See the blog post 59 * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> 60 * Fragments For All</a> for more details. 61 */ 62 public abstract class FragmentManager { 63 /** 64 * Representation of an entry on the fragment back stack, as created 65 * with {@link FragmentTransaction#addToBackStack(String) 66 * FragmentTransaction.addToBackStack()}. Entries can later be 67 * retrieved with {@link FragmentManager#getBackStackEntryAt(int) 68 * FragmentManager.getBackStackEntry()}. 69 * 70 * <p>Note that you should never hold on to a BackStackEntry object; 71 * the identifier as returned by {@link #getId} is the only thing that 72 * will be persisted across activity instances. 73 */ 74 public interface BackStackEntry { 75 /** 76 * Return the unique identifier for the entry. This is the only 77 * representation of the entry that will persist across activity 78 * instances. 79 */ 80 public int getId(); 81 82 /** 83 * Get the name that was supplied to 84 * {@link FragmentTransaction#addToBackStack(String) 85 * FragmentTransaction.addToBackStack(String)} when creating this entry. 86 */ 87 public String getName(); 88 89 /** 90 * Return the full bread crumb title resource identifier for the entry, 91 * or 0 if it does not have one. 92 */ 93 public int getBreadCrumbTitleRes(); 94 95 /** 96 * Return the short bread crumb title resource identifier for the entry, 97 * or 0 if it does not have one. 98 */ 99 public int getBreadCrumbShortTitleRes(); 100 101 /** 102 * Return the full bread crumb title for the entry, or null if it 103 * does not have one. 104 */ 105 public CharSequence getBreadCrumbTitle(); 106 107 /** 108 * Return the short bread crumb title for the entry, or null if it 109 * does not have one. 110 */ 111 public CharSequence getBreadCrumbShortTitle(); 112 } 113 114 /** 115 * Interface to watch for changes to the back stack. 116 */ 117 public interface OnBackStackChangedListener { 118 /** 119 * Called whenever the contents of the back stack change. 120 */ 121 public void onBackStackChanged(); 122 } 123 124 /** 125 * Start a series of edit operations on the Fragments associated with 126 * this FragmentManager. 127 * 128 * <p>Note: A fragment transaction can only be created/committed prior 129 * to an activity saving its state. If you try to commit a transaction 130 * after {@link Activity#onSaveInstanceState Activity.onSaveInstanceState()} 131 * (and prior to a following {@link Activity#onStart Activity.onStart} 132 * or {@link Activity#onResume Activity.onResume()}, you will get an error. 133 * This is because the framework takes care of saving your current fragments 134 * in the state, and if changes are made after the state is saved then they 135 * will be lost.</p> 136 */ 137 public abstract FragmentTransaction beginTransaction(); 138 139 /** @hide -- remove once prebuilts are in. */ 140 @Deprecated 141 public FragmentTransaction openTransaction() { 142 return beginTransaction(); 143 } 144 145 /** 146 * After a {@link FragmentTransaction} is committed with 147 * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it 148 * is scheduled to be executed asynchronously on the process's main thread. 149 * If you want to immediately executing any such pending operations, you 150 * can call this function (only from the main thread) to do so. Note that 151 * all callbacks and other related behavior will be done from within this 152 * call, so be careful about where this is called from. 153 * 154 * @return Returns true if there were any pending transactions to be 155 * executed. 156 */ 157 public abstract boolean executePendingTransactions(); 158 159 /** 160 * Finds a fragment that was identified by the given id either when inflated 161 * from XML or as the container ID when added in a transaction. This first 162 * searches through fragments that are currently added to the manager's 163 * activity; if no such fragment is found, then all fragments currently 164 * on the back stack associated with this ID are searched. 165 * @return The fragment if found or null otherwise. 166 */ 167 public abstract Fragment findFragmentById(int id); 168 169 /** 170 * Finds a fragment that was identified by the given tag either when inflated 171 * from XML or as supplied when added in a transaction. This first 172 * searches through fragments that are currently added to the manager's 173 * activity; if no such fragment is found, then all fragments currently 174 * on the back stack are searched. 175 * @return The fragment if found or null otherwise. 176 */ 177 public abstract Fragment findFragmentByTag(String tag); 178 179 /** 180 * Flag for {@link #popBackStack(String, int)} 181 * and {@link #popBackStack(int, int)}: If set, and the name or ID of 182 * a back stack entry has been supplied, then all matching entries will 183 * be consumed until one that doesn't match is found or the bottom of 184 * the stack is reached. Otherwise, all entries up to but not including that entry 185 * will be removed. 186 */ 187 public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; 188 189 /** 190 * Pop the top state off the back stack. This function is asynchronous -- it 191 * enqueues the request to pop, but the action will not be performed until the 192 * application returns to its event loop. 193 */ 194 public abstract void popBackStack(); 195 196 /** 197 * Like {@link #popBackStack()}, but performs the operation immediately 198 * inside of the call. This is like calling {@link #executePendingTransactions()} 199 * afterwards. 200 * @return Returns true if there was something popped, else false. 201 */ 202 public abstract boolean popBackStackImmediate(); 203 204 /** 205 * Pop the last fragment transition from the manager's fragment 206 * back stack. If there is nothing to pop, false is returned. 207 * This function is asynchronous -- it enqueues the 208 * request to pop, but the action will not be performed until the application 209 * returns to its event loop. 210 * 211 * @param name If non-null, this is the name of a previous back state 212 * to look for; if found, all states up to that state will be popped. The 213 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 214 * the named state itself is popped. If null, only the top state is popped. 215 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 216 */ 217 public abstract void popBackStack(String name, int flags); 218 219 /** 220 * Like {@link #popBackStack(String, int)}, but performs the operation immediately 221 * inside of the call. This is like calling {@link #executePendingTransactions()} 222 * afterwards. 223 * @return Returns true if there was something popped, else false. 224 */ 225 public abstract boolean popBackStackImmediate(String name, int flags); 226 227 /** 228 * Pop all back stack states up to the one with the given identifier. 229 * This function is asynchronous -- it enqueues the 230 * request to pop, but the action will not be performed until the application 231 * returns to its event loop. 232 * 233 * @param id Identifier of the stated to be popped. If no identifier exists, 234 * false is returned. 235 * The identifier is the number returned by 236 * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The 237 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 238 * the named state itself is popped. 239 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 240 */ 241 public abstract void popBackStack(int id, int flags); 242 243 /** 244 * Like {@link #popBackStack(int, int)}, but performs the operation immediately 245 * inside of the call. This is like calling {@link #executePendingTransactions()} 246 * afterwards. 247 * @return Returns true if there was something popped, else false. 248 */ 249 public abstract boolean popBackStackImmediate(int id, int flags); 250 251 /** 252 * Return the number of entries currently in the back stack. 253 */ 254 public abstract int getBackStackEntryCount(); 255 256 /** 257 * Return the BackStackEntry at index <var>index</var> in the back stack; 258 * entries start index 0 being the bottom of the stack. 259 */ 260 public abstract BackStackEntry getBackStackEntryAt(int index); 261 262 /** 263 * Add a new listener for changes to the fragment back stack. 264 */ 265 public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener); 266 267 /** 268 * Remove a listener that was previously added with 269 * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}. 270 */ 271 public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener); 272 273 /** 274 * Put a reference to a fragment in a Bundle. This Bundle can be 275 * persisted as saved state, and when later restoring 276 * {@link #getFragment(Bundle, String)} will return the current 277 * instance of the same fragment. 278 * 279 * @param bundle The bundle in which to put the fragment reference. 280 * @param key The name of the entry in the bundle. 281 * @param fragment The Fragment whose reference is to be stored. 282 */ 283 public abstract void putFragment(Bundle bundle, String key, Fragment fragment); 284 285 /** 286 * Retrieve the current Fragment instance for a reference previously 287 * placed with {@link #putFragment(Bundle, String, Fragment)}. 288 * 289 * @param bundle The bundle from which to retrieve the fragment reference. 290 * @param key The name of the entry in the bundle. 291 * @return Returns the current Fragment instance that is associated with 292 * the given reference. 293 */ 294 public abstract Fragment getFragment(Bundle bundle, String key); 295 296 /** 297 * Save the current instance state of the given Fragment. This can be 298 * used later when creating a new instance of the Fragment and adding 299 * it to the fragment manager, to have it create itself to match the 300 * current state returned here. Note that there are limits on how 301 * this can be used: 302 * 303 * <ul> 304 * <li>The Fragment must currently be attached to the FragmentManager. 305 * <li>A new Fragment created using this saved state must be the same class 306 * type as the Fragment it was created from. 307 * <li>The saved state can not contain dependencies on other fragments -- 308 * that is it can't use {@link #putFragment(Bundle, String, Fragment)} to 309 * store a fragment reference because that reference may not be valid when 310 * this saved state is later used. Likewise the Fragment's target and 311 * result code are not included in this state. 312 * </ul> 313 * 314 * @param f The Fragment whose state is to be saved. 315 * @return The generated state. This will be null if there was no 316 * interesting state created by the fragment. 317 */ 318 public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f); 319 320 /** 321 * Returns true if the final {@link Activity#onDestroy() Activity.onDestroy()} 322 * call has been made on the FragmentManager's Activity, so this instance is now dead. 323 */ 324 public abstract boolean isDestroyed(); 325 326 /** 327 * Print the FragmentManager's state into the given stream. 328 * 329 * @param prefix Text to print at the front of each line. 330 * @param fd The raw file descriptor that the dump is being sent to. 331 * @param writer A PrintWriter to which the dump is to be set. 332 * @param args Additional arguments to the dump request. 333 */ 334 public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); 335 336 /** 337 * Control whether the framework's internal fragment manager debugging 338 * logs are turned on. If enabled, you will see output in logcat as 339 * the framework performs fragment operations. 340 */ 341 public static void enableDebugLogging(boolean enabled) { 342 FragmentManagerImpl.DEBUG = enabled; 343 } 344 345 /** 346 * Invalidate the attached activity's options menu as necessary. 347 * This may end up being deferred until we move to the resumed state. 348 */ 349 public void invalidateOptionsMenu() { } 350 } 351 352 final class FragmentManagerState implements Parcelable { 353 FragmentState[] mActive; 354 int[] mAdded; 355 BackStackState[] mBackStack; 356 357 public FragmentManagerState() { 358 } 359 360 public FragmentManagerState(Parcel in) { 361 mActive = in.createTypedArray(FragmentState.CREATOR); 362 mAdded = in.createIntArray(); 363 mBackStack = in.createTypedArray(BackStackState.CREATOR); 364 } 365 366 public int describeContents() { 367 return 0; 368 } 369 370 public void writeToParcel(Parcel dest, int flags) { 371 dest.writeTypedArray(mActive, flags); 372 dest.writeIntArray(mAdded); 373 dest.writeTypedArray(mBackStack, flags); 374 } 375 376 public static final Parcelable.Creator<FragmentManagerState> CREATOR 377 = new Parcelable.Creator<FragmentManagerState>() { 378 public FragmentManagerState createFromParcel(Parcel in) { 379 return new FragmentManagerState(in); 380 } 381 382 public FragmentManagerState[] newArray(int size) { 383 return new FragmentManagerState[size]; 384 } 385 }; 386 } 387 388 /** 389 * Callbacks from FragmentManagerImpl to its container. 390 */ 391 interface FragmentContainer { 392 public View findViewById(int id); 393 } 394 395 /** 396 * Container for fragments associated with an activity. 397 */ 398 final class FragmentManagerImpl extends FragmentManager { 399 static boolean DEBUG = false; 400 static final String TAG = "FragmentManager"; 401 402 static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state"; 403 static final String TARGET_STATE_TAG = "android:target_state"; 404 static final String VIEW_STATE_TAG = "android:view_state"; 405 static final String USER_VISIBLE_HINT_TAG = "android:user_visible_hint"; 406 407 ArrayList<Runnable> mPendingActions; 408 Runnable[] mTmpActions; 409 boolean mExecutingActions; 410 411 ArrayList<Fragment> mActive; 412 ArrayList<Fragment> mAdded; 413 ArrayList<Integer> mAvailIndices; 414 ArrayList<BackStackRecord> mBackStack; 415 ArrayList<Fragment> mCreatedMenus; 416 417 // Must be accessed while locked. 418 ArrayList<BackStackRecord> mBackStackIndices; 419 ArrayList<Integer> mAvailBackStackIndices; 420 421 ArrayList<OnBackStackChangedListener> mBackStackChangeListeners; 422 423 int mCurState = Fragment.INITIALIZING; 424 Activity mActivity; 425 FragmentContainer mContainer; 426 Fragment mParent; 427 428 boolean mNeedMenuInvalidate; 429 boolean mStateSaved; 430 boolean mDestroyed; 431 String mNoTransactionsBecause; 432 boolean mHavePendingDeferredStart; 433 434 // Temporary vars for state save and restore. 435 Bundle mStateBundle = null; 436 SparseArray<Parcelable> mStateArray = null; 437 438 Runnable mExecCommit = new Runnable() { 439 @Override 440 public void run() { 441 execPendingActions(); 442 } 443 }; 444 445 private void throwException(RuntimeException ex) { 446 Log.e(TAG, ex.getMessage()); 447 LogWriter logw = new LogWriter(Log.ERROR, TAG); 448 PrintWriter pw = new PrintWriter(logw); 449 if (mActivity != null) { 450 Log.e(TAG, "Activity state:"); 451 try { 452 mActivity.dump(" ", null, pw, new String[] { }); 453 } catch (Exception e) { 454 Log.e(TAG, "Failed dumping state", e); 455 } 456 } else { 457 Log.e(TAG, "Fragment manager state:"); 458 try { 459 dump(" ", null, pw, new String[] { }); 460 } catch (Exception e) { 461 Log.e(TAG, "Failed dumping state", e); 462 } 463 } 464 throw ex; 465 } 466 467 @Override 468 public FragmentTransaction beginTransaction() { 469 return new BackStackRecord(this); 470 } 471 472 @Override 473 public boolean executePendingTransactions() { 474 return execPendingActions(); 475 } 476 477 @Override 478 public void popBackStack() { 479 enqueueAction(new Runnable() { 480 @Override public void run() { 481 popBackStackState(mActivity.mHandler, null, -1, 0); 482 } 483 }, false); 484 } 485 486 @Override 487 public boolean popBackStackImmediate() { 488 checkStateLoss(); 489 executePendingTransactions(); 490 return popBackStackState(mActivity.mHandler, null, -1, 0); 491 } 492 493 @Override 494 public void popBackStack(final String name, final int flags) { 495 enqueueAction(new Runnable() { 496 @Override public void run() { 497 popBackStackState(mActivity.mHandler, name, -1, flags); 498 } 499 }, false); 500 } 501 502 @Override 503 public boolean popBackStackImmediate(String name, int flags) { 504 checkStateLoss(); 505 executePendingTransactions(); 506 return popBackStackState(mActivity.mHandler, name, -1, flags); 507 } 508 509 @Override 510 public void popBackStack(final int id, final int flags) { 511 if (id < 0) { 512 throw new IllegalArgumentException("Bad id: " + id); 513 } 514 enqueueAction(new Runnable() { 515 @Override public void run() { 516 popBackStackState(mActivity.mHandler, null, id, flags); 517 } 518 }, false); 519 } 520 521 @Override 522 public boolean popBackStackImmediate(int id, int flags) { 523 checkStateLoss(); 524 executePendingTransactions(); 525 if (id < 0) { 526 throw new IllegalArgumentException("Bad id: " + id); 527 } 528 return popBackStackState(mActivity.mHandler, null, id, flags); 529 } 530 531 @Override 532 public int getBackStackEntryCount() { 533 return mBackStack != null ? mBackStack.size() : 0; 534 } 535 536 @Override 537 public BackStackEntry getBackStackEntryAt(int index) { 538 return mBackStack.get(index); 539 } 540 541 @Override 542 public void addOnBackStackChangedListener(OnBackStackChangedListener listener) { 543 if (mBackStackChangeListeners == null) { 544 mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>(); 545 } 546 mBackStackChangeListeners.add(listener); 547 } 548 549 @Override 550 public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) { 551 if (mBackStackChangeListeners != null) { 552 mBackStackChangeListeners.remove(listener); 553 } 554 } 555 556 @Override 557 public void putFragment(Bundle bundle, String key, Fragment fragment) { 558 if (fragment.mIndex < 0) { 559 throwException(new IllegalStateException("Fragment " + fragment 560 + " is not currently in the FragmentManager")); 561 } 562 bundle.putInt(key, fragment.mIndex); 563 } 564 565 @Override 566 public Fragment getFragment(Bundle bundle, String key) { 567 int index = bundle.getInt(key, -1); 568 if (index == -1) { 569 return null; 570 } 571 if (index >= mActive.size()) { 572 throwException(new IllegalStateException("Fragement no longer exists for key " 573 + key + ": index " + index)); 574 } 575 Fragment f = mActive.get(index); 576 if (f == null) { 577 throwException(new IllegalStateException("Fragement no longer exists for key " 578 + key + ": index " + index)); 579 } 580 return f; 581 } 582 583 @Override 584 public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) { 585 if (fragment.mIndex < 0) { 586 throwException(new IllegalStateException("Fragment " + fragment 587 + " is not currently in the FragmentManager")); 588 } 589 if (fragment.mState > Fragment.INITIALIZING) { 590 Bundle result = saveFragmentBasicState(fragment); 591 return result != null ? new Fragment.SavedState(result) : null; 592 } 593 return null; 594 } 595 596 @Override 597 public boolean isDestroyed() { 598 return mDestroyed; 599 } 600 601 @Override 602 public String toString() { 603 StringBuilder sb = new StringBuilder(128); 604 sb.append("FragmentManager{"); 605 sb.append(Integer.toHexString(System.identityHashCode(this))); 606 sb.append(" in "); 607 if (mParent != null) { 608 DebugUtils.buildShortClassTag(mParent, sb); 609 } else { 610 DebugUtils.buildShortClassTag(mActivity, sb); 611 } 612 sb.append("}}"); 613 return sb.toString(); 614 } 615 616 @Override 617 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 618 String innerPrefix = prefix + " "; 619 620 int N; 621 if (mActive != null) { 622 N = mActive.size(); 623 if (N > 0) { 624 writer.print(prefix); writer.print("Active Fragments in "); 625 writer.print(Integer.toHexString(System.identityHashCode(this))); 626 writer.println(":"); 627 for (int i=0; i<N; i++) { 628 Fragment f = mActive.get(i); 629 writer.print(prefix); writer.print(" #"); writer.print(i); 630 writer.print(": "); writer.println(f); 631 if (f != null) { 632 f.dump(innerPrefix, fd, writer, args); 633 } 634 } 635 } 636 } 637 638 if (mAdded != null) { 639 N = mAdded.size(); 640 if (N > 0) { 641 writer.print(prefix); writer.println("Added Fragments:"); 642 for (int i=0; i<N; i++) { 643 Fragment f = mAdded.get(i); 644 writer.print(prefix); writer.print(" #"); writer.print(i); 645 writer.print(": "); writer.println(f.toString()); 646 } 647 } 648 } 649 650 if (mCreatedMenus != null) { 651 N = mCreatedMenus.size(); 652 if (N > 0) { 653 writer.print(prefix); writer.println("Fragments Created Menus:"); 654 for (int i=0; i<N; i++) { 655 Fragment f = mCreatedMenus.get(i); 656 writer.print(prefix); writer.print(" #"); writer.print(i); 657 writer.print(": "); writer.println(f.toString()); 658 } 659 } 660 } 661 662 if (mBackStack != null) { 663 N = mBackStack.size(); 664 if (N > 0) { 665 writer.print(prefix); writer.println("Back Stack:"); 666 for (int i=0; i<N; i++) { 667 BackStackRecord bs = mBackStack.get(i); 668 writer.print(prefix); writer.print(" #"); writer.print(i); 669 writer.print(": "); writer.println(bs.toString()); 670 bs.dump(innerPrefix, fd, writer, args); 671 } 672 } 673 } 674 675 synchronized (this) { 676 if (mBackStackIndices != null) { 677 N = mBackStackIndices.size(); 678 if (N > 0) { 679 writer.print(prefix); writer.println("Back Stack Indices:"); 680 for (int i=0; i<N; i++) { 681 BackStackRecord bs = mBackStackIndices.get(i); 682 writer.print(prefix); writer.print(" #"); writer.print(i); 683 writer.print(": "); writer.println(bs); 684 } 685 } 686 } 687 688 if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) { 689 writer.print(prefix); writer.print("mAvailBackStackIndices: "); 690 writer.println(Arrays.toString(mAvailBackStackIndices.toArray())); 691 } 692 } 693 694 if (mPendingActions != null) { 695 N = mPendingActions.size(); 696 if (N > 0) { 697 writer.print(prefix); writer.println("Pending Actions:"); 698 for (int i=0; i<N; i++) { 699 Runnable r = mPendingActions.get(i); 700 writer.print(prefix); writer.print(" #"); writer.print(i); 701 writer.print(": "); writer.println(r); 702 } 703 } 704 } 705 706 writer.print(prefix); writer.println("FragmentManager misc state:"); 707 writer.print(prefix); writer.print(" mActivity="); writer.println(mActivity); 708 writer.print(prefix); writer.print(" mContainer="); writer.println(mContainer); 709 if (mParent != null) { 710 writer.print(prefix); writer.print(" mParent="); writer.println(mParent); 711 } 712 writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState); 713 writer.print(" mStateSaved="); writer.print(mStateSaved); 714 writer.print(" mDestroyed="); writer.println(mDestroyed); 715 if (mNeedMenuInvalidate) { 716 writer.print(prefix); writer.print(" mNeedMenuInvalidate="); 717 writer.println(mNeedMenuInvalidate); 718 } 719 if (mNoTransactionsBecause != null) { 720 writer.print(prefix); writer.print(" mNoTransactionsBecause="); 721 writer.println(mNoTransactionsBecause); 722 } 723 if (mAvailIndices != null && mAvailIndices.size() > 0) { 724 writer.print(prefix); writer.print(" mAvailIndices: "); 725 writer.println(Arrays.toString(mAvailIndices.toArray())); 726 } 727 } 728 729 Animator loadAnimator(Fragment fragment, int transit, boolean enter, 730 int transitionStyle) { 731 Animator animObj = fragment.onCreateAnimator(transit, enter, 732 fragment.mNextAnim); 733 if (animObj != null) { 734 return animObj; 735 } 736 737 if (fragment.mNextAnim != 0) { 738 Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim); 739 if (anim != null) { 740 return anim; 741 } 742 } 743 744 if (transit == 0) { 745 return null; 746 } 747 748 int styleIndex = transitToStyleIndex(transit, enter); 749 if (styleIndex < 0) { 750 return null; 751 } 752 753 if (transitionStyle == 0 && mActivity.getWindow() != null) { 754 transitionStyle = mActivity.getWindow().getAttributes().windowAnimations; 755 } 756 if (transitionStyle == 0) { 757 return null; 758 } 759 760 TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle, 761 com.android.internal.R.styleable.FragmentAnimation); 762 int anim = attrs.getResourceId(styleIndex, 0); 763 attrs.recycle(); 764 765 if (anim == 0) { 766 return null; 767 } 768 769 return AnimatorInflater.loadAnimator(mActivity, anim); 770 } 771 772 public void performPendingDeferredStart(Fragment f) { 773 if (f.mDeferStart) { 774 if (mExecutingActions) { 775 // Wait until we're done executing our pending transactions 776 mHavePendingDeferredStart = true; 777 return; 778 } 779 f.mDeferStart = false; 780 moveToState(f, mCurState, 0, 0, false); 781 } 782 } 783 784 void moveToState(Fragment f, int newState, int transit, int transitionStyle, 785 boolean keepActive) { 786 if (DEBUG && false) Log.v(TAG, "moveToState: " + f 787 + " oldState=" + f.mState + " newState=" + newState 788 + " mRemoving=" + f.mRemoving + " Callers=" + Debug.getCallers(5)); 789 790 // Fragments that are not currently added will sit in the onCreate() state. 791 if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) { 792 newState = Fragment.CREATED; 793 } 794 if (f.mRemoving && newState > f.mState) { 795 // While removing a fragment, we can't change it to a higher state. 796 newState = f.mState; 797 } 798 // Defer start if requested; don't allow it to move to STARTED or higher 799 // if it's not already started. 800 if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) { 801 newState = Fragment.STOPPED; 802 } 803 if (f.mState < newState) { 804 // For fragments that are created from a layout, when restoring from 805 // state we don't want to allow them to be created until they are 806 // being reloaded from the layout. 807 if (f.mFromLayout && !f.mInLayout) { 808 return; 809 } 810 if (f.mAnimatingAway != null) { 811 // The fragment is currently being animated... but! Now we 812 // want to move our state back up. Give up on waiting for the 813 // animation, move to whatever the final state should be once 814 // the animation is done, and then we can proceed from there. 815 f.mAnimatingAway = null; 816 moveToState(f, f.mStateAfterAnimating, 0, 0, true); 817 } 818 switch (f.mState) { 819 case Fragment.INITIALIZING: 820 if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); 821 if (f.mSavedFragmentState != null) { 822 f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( 823 FragmentManagerImpl.VIEW_STATE_TAG); 824 f.mTarget = getFragment(f.mSavedFragmentState, 825 FragmentManagerImpl.TARGET_STATE_TAG); 826 if (f.mTarget != null) { 827 f.mTargetRequestCode = f.mSavedFragmentState.getInt( 828 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); 829 } 830 f.mUserVisibleHint = f.mSavedFragmentState.getBoolean( 831 FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true); 832 if (!f.mUserVisibleHint) { 833 f.mDeferStart = true; 834 if (newState > Fragment.STOPPED) { 835 newState = Fragment.STOPPED; 836 } 837 } 838 } 839 f.mActivity = mActivity; 840 f.mParentFragment = mParent; 841 f.mFragmentManager = mParent != null 842 ? mParent.mChildFragmentManager : mActivity.mFragments; 843 f.mCalled = false; 844 f.onAttach(mActivity); 845 if (!f.mCalled) { 846 throw new SuperNotCalledException("Fragment " + f 847 + " did not call through to super.onAttach()"); 848 } 849 if (f.mParentFragment == null) { 850 mActivity.onAttachFragment(f); 851 } 852 853 if (!f.mRetaining) { 854 f.performCreate(f.mSavedFragmentState); 855 } 856 f.mRetaining = false; 857 if (f.mFromLayout) { 858 // For fragments that are part of the content view 859 // layout, we need to instantiate the view immediately 860 // and the inflater will take care of adding it. 861 f.mView = f.performCreateView(f.getLayoutInflater( 862 f.mSavedFragmentState), null, f.mSavedFragmentState); 863 if (f.mView != null) { 864 f.mView.setSaveFromParentEnabled(false); 865 if (f.mHidden) f.mView.setVisibility(View.GONE); 866 f.onViewCreated(f.mView, f.mSavedFragmentState); 867 } 868 } 869 case Fragment.CREATED: 870 if (newState > Fragment.CREATED) { 871 if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f); 872 if (!f.mFromLayout) { 873 ViewGroup container = null; 874 if (f.mContainerId != 0) { 875 container = (ViewGroup)mContainer.findViewById(f.mContainerId); 876 if (container == null && !f.mRestored) { 877 throwException(new IllegalArgumentException( 878 "No view found for id 0x" 879 + Integer.toHexString(f.mContainerId) + " (" 880 + f.getResources().getResourceName(f.mContainerId) 881 + ") for fragment " + f)); 882 } 883 } 884 f.mContainer = container; 885 f.mView = f.performCreateView(f.getLayoutInflater( 886 f.mSavedFragmentState), container, f.mSavedFragmentState); 887 if (f.mView != null) { 888 f.mView.setSaveFromParentEnabled(false); 889 if (container != null) { 890 Animator anim = loadAnimator(f, transit, true, 891 transitionStyle); 892 if (anim != null) { 893 anim.setTarget(f.mView); 894 anim.start(); 895 } 896 container.addView(f.mView); 897 } 898 if (f.mHidden) f.mView.setVisibility(View.GONE); 899 f.onViewCreated(f.mView, f.mSavedFragmentState); 900 } 901 } 902 903 f.performActivityCreated(f.mSavedFragmentState); 904 if (f.mView != null) { 905 f.restoreViewState(f.mSavedFragmentState); 906 } 907 f.mSavedFragmentState = null; 908 } 909 case Fragment.ACTIVITY_CREATED: 910 case Fragment.STOPPED: 911 if (newState > Fragment.STOPPED) { 912 if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); 913 f.performStart(); 914 } 915 case Fragment.STARTED: 916 if (newState > Fragment.STARTED) { 917 if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); 918 f.mResumed = true; 919 f.performResume(); 920 // Get rid of this in case we saved it and never needed it. 921 f.mSavedFragmentState = null; 922 f.mSavedViewState = null; 923 } 924 } 925 } else if (f.mState > newState) { 926 switch (f.mState) { 927 case Fragment.RESUMED: 928 if (newState < Fragment.RESUMED) { 929 if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); 930 f.performPause(); 931 f.mResumed = false; 932 } 933 case Fragment.STARTED: 934 if (newState < Fragment.STARTED) { 935 if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); 936 f.performStop(); 937 } 938 case Fragment.STOPPED: 939 case Fragment.ACTIVITY_CREATED: 940 if (newState < Fragment.ACTIVITY_CREATED) { 941 if (DEBUG) Log.v(TAG, "movefrom ACTIVITY_CREATED: " + f); 942 if (f.mView != null) { 943 // Need to save the current view state if not 944 // done already. 945 if (!mActivity.isFinishing() && f.mSavedViewState == null) { 946 saveFragmentViewState(f); 947 } 948 } 949 f.performDestroyView(); 950 if (f.mView != null && f.mContainer != null) { 951 Animator anim = null; 952 if (mCurState > Fragment.INITIALIZING && !mDestroyed) { 953 anim = loadAnimator(f, transit, false, 954 transitionStyle); 955 } 956 if (anim != null) { 957 final ViewGroup container = f.mContainer; 958 final View view = f.mView; 959 final Fragment fragment = f; 960 container.startViewTransition(view); 961 f.mAnimatingAway = anim; 962 f.mStateAfterAnimating = newState; 963 anim.addListener(new AnimatorListenerAdapter() { 964 @Override 965 public void onAnimationEnd(Animator anim) { 966 container.endViewTransition(view); 967 if (fragment.mAnimatingAway != null) { 968 fragment.mAnimatingAway = null; 969 moveToState(fragment, fragment.mStateAfterAnimating, 970 0, 0, false); 971 } 972 } 973 }); 974 anim.setTarget(f.mView); 975 anim.start(); 976 977 } 978 f.mContainer.removeView(f.mView); 979 } 980 f.mContainer = null; 981 f.mView = null; 982 } 983 case Fragment.CREATED: 984 if (newState < Fragment.CREATED) { 985 if (mDestroyed) { 986 if (f.mAnimatingAway != null) { 987 // The fragment's containing activity is 988 // being destroyed, but this fragment is 989 // currently animating away. Stop the 990 // animation right now -- it is not needed, 991 // and we can't wait any more on destroying 992 // the fragment. 993 Animator anim = f.mAnimatingAway; 994 f.mAnimatingAway = null; 995 anim.cancel(); 996 } 997 } 998 if (f.mAnimatingAway != null) { 999 // We are waiting for the fragment's view to finish 1000 // animating away. Just make a note of the state 1001 // the fragment now should move to once the animation 1002 // is done. 1003 f.mStateAfterAnimating = newState; 1004 newState = Fragment.CREATED; 1005 } else { 1006 if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); 1007 if (!f.mRetaining) { 1008 f.performDestroy(); 1009 } 1010 1011 f.mCalled = false; 1012 f.onDetach(); 1013 if (!f.mCalled) { 1014 throw new SuperNotCalledException("Fragment " + f 1015 + " did not call through to super.onDetach()"); 1016 } 1017 if (!keepActive) { 1018 if (!f.mRetaining) { 1019 makeInactive(f); 1020 } else { 1021 f.mActivity = null; 1022 f.mParentFragment = null; 1023 f.mFragmentManager = null; 1024 } 1025 } 1026 } 1027 } 1028 } 1029 } 1030 1031 f.mState = newState; 1032 } 1033 1034 void moveToState(Fragment f) { 1035 moveToState(f, mCurState, 0, 0, false); 1036 } 1037 1038 void moveToState(int newState, boolean always) { 1039 moveToState(newState, 0, 0, always); 1040 } 1041 1042 void moveToState(int newState, int transit, int transitStyle, boolean always) { 1043 if (mActivity == null && newState != Fragment.INITIALIZING) { 1044 throw new IllegalStateException("No activity"); 1045 } 1046 1047 if (!always && mCurState == newState) { 1048 return; 1049 } 1050 1051 mCurState = newState; 1052 if (mActive != null) { 1053 boolean loadersRunning = false; 1054 for (int i=0; i<mActive.size(); i++) { 1055 Fragment f = mActive.get(i); 1056 if (f != null) { 1057 moveToState(f, newState, transit, transitStyle, false); 1058 if (f.mLoaderManager != null) { 1059 loadersRunning |= f.mLoaderManager.hasRunningLoaders(); 1060 } 1061 } 1062 } 1063 1064 if (!loadersRunning) { 1065 startPendingDeferredFragments(); 1066 } 1067 1068 if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) { 1069 mActivity.invalidateOptionsMenu(); 1070 mNeedMenuInvalidate = false; 1071 } 1072 } 1073 } 1074 1075 void startPendingDeferredFragments() { 1076 if (mActive == null) return; 1077 1078 for (int i=0; i<mActive.size(); i++) { 1079 Fragment f = mActive.get(i); 1080 if (f != null) { 1081 performPendingDeferredStart(f); 1082 } 1083 } 1084 } 1085 1086 void makeActive(Fragment f) { 1087 if (f.mIndex >= 0) { 1088 return; 1089 } 1090 1091 if (mAvailIndices == null || mAvailIndices.size() <= 0) { 1092 if (mActive == null) { 1093 mActive = new ArrayList<Fragment>(); 1094 } 1095 f.setIndex(mActive.size(), mParent); 1096 mActive.add(f); 1097 1098 } else { 1099 f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent); 1100 mActive.set(f.mIndex, f); 1101 } 1102 if (DEBUG) Log.v(TAG, "Allocated fragment index " + f); 1103 } 1104 1105 void makeInactive(Fragment f) { 1106 if (f.mIndex < 0) { 1107 return; 1108 } 1109 1110 if (DEBUG) Log.v(TAG, "Freeing fragment index " + f); 1111 mActive.set(f.mIndex, null); 1112 if (mAvailIndices == null) { 1113 mAvailIndices = new ArrayList<Integer>(); 1114 } 1115 mAvailIndices.add(f.mIndex); 1116 mActivity.invalidateFragment(f.mWho); 1117 f.initState(); 1118 } 1119 1120 public void addFragment(Fragment fragment, boolean moveToStateNow) { 1121 if (mAdded == null) { 1122 mAdded = new ArrayList<Fragment>(); 1123 } 1124 if (DEBUG) Log.v(TAG, "add: " + fragment); 1125 makeActive(fragment); 1126 if (!fragment.mDetached) { 1127 if (mAdded.contains(fragment)) { 1128 throw new IllegalStateException("Fragment already added: " + fragment); 1129 } 1130 mAdded.add(fragment); 1131 fragment.mAdded = true; 1132 fragment.mRemoving = false; 1133 if (fragment.mHasMenu && fragment.mMenuVisible) { 1134 mNeedMenuInvalidate = true; 1135 } 1136 if (moveToStateNow) { 1137 moveToState(fragment); 1138 } 1139 } 1140 } 1141 1142 public void removeFragment(Fragment fragment, int transition, int transitionStyle) { 1143 if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); 1144 final boolean inactive = !fragment.isInBackStack(); 1145 if (!fragment.mDetached || inactive) { 1146 if (false) { 1147 // Would be nice to catch a bad remove here, but we need 1148 // time to test this to make sure we aren't crashes cases 1149 // where it is not a problem. 1150 if (!mAdded.contains(fragment)) { 1151 throw new IllegalStateException("Fragment not added: " + fragment); 1152 } 1153 } 1154 if (mAdded != null) { 1155 mAdded.remove(fragment); 1156 } 1157 if (fragment.mHasMenu && fragment.mMenuVisible) { 1158 mNeedMenuInvalidate = true; 1159 } 1160 fragment.mAdded = false; 1161 fragment.mRemoving = true; 1162 moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, 1163 transition, transitionStyle, false); 1164 } 1165 } 1166 1167 public void hideFragment(Fragment fragment, int transition, int transitionStyle) { 1168 if (DEBUG) Log.v(TAG, "hide: " + fragment); 1169 if (!fragment.mHidden) { 1170 fragment.mHidden = true; 1171 if (fragment.mView != null) { 1172 Animator anim = loadAnimator(fragment, transition, true, 1173 transitionStyle); 1174 if (anim != null) { 1175 anim.setTarget(fragment.mView); 1176 // Delay the actual hide operation until the animation finishes, otherwise 1177 // the fragment will just immediately disappear 1178 final Fragment finalFragment = fragment; 1179 anim.addListener(new AnimatorListenerAdapter() { 1180 @Override 1181 public void onAnimationEnd(Animator animation) { 1182 if (finalFragment.mView != null) { 1183 finalFragment.mView.setVisibility(View.GONE); 1184 } 1185 } 1186 }); 1187 anim.start(); 1188 } else { 1189 fragment.mView.setVisibility(View.GONE); 1190 } 1191 } 1192 if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { 1193 mNeedMenuInvalidate = true; 1194 } 1195 fragment.onHiddenChanged(true); 1196 } 1197 } 1198 1199 public void showFragment(Fragment fragment, int transition, int transitionStyle) { 1200 if (DEBUG) Log.v(TAG, "show: " + fragment); 1201 if (fragment.mHidden) { 1202 fragment.mHidden = false; 1203 if (fragment.mView != null) { 1204 Animator anim = loadAnimator(fragment, transition, true, 1205 transitionStyle); 1206 if (anim != null) { 1207 anim.setTarget(fragment.mView); 1208 anim.start(); 1209 } 1210 fragment.mView.setVisibility(View.VISIBLE); 1211 } 1212 if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { 1213 mNeedMenuInvalidate = true; 1214 } 1215 fragment.onHiddenChanged(false); 1216 } 1217 } 1218 1219 public void detachFragment(Fragment fragment, int transition, int transitionStyle) { 1220 if (DEBUG) Log.v(TAG, "detach: " + fragment); 1221 if (!fragment.mDetached) { 1222 fragment.mDetached = true; 1223 if (fragment.mAdded) { 1224 // We are not already in back stack, so need to remove the fragment. 1225 if (mAdded != null) { 1226 if (DEBUG) Log.v(TAG, "remove from detach: " + fragment); 1227 mAdded.remove(fragment); 1228 } 1229 if (fragment.mHasMenu && fragment.mMenuVisible) { 1230 mNeedMenuInvalidate = true; 1231 } 1232 fragment.mAdded = false; 1233 moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false); 1234 } 1235 } 1236 } 1237 1238 public void attachFragment(Fragment fragment, int transition, int transitionStyle) { 1239 if (DEBUG) Log.v(TAG, "attach: " + fragment); 1240 if (fragment.mDetached) { 1241 fragment.mDetached = false; 1242 if (!fragment.mAdded) { 1243 if (mAdded == null) { 1244 mAdded = new ArrayList<Fragment>(); 1245 } 1246 if (mAdded.contains(fragment)) { 1247 throw new IllegalStateException("Fragment already added: " + fragment); 1248 } 1249 if (DEBUG) Log.v(TAG, "add from attach: " + fragment); 1250 mAdded.add(fragment); 1251 fragment.mAdded = true; 1252 if (fragment.mHasMenu && fragment.mMenuVisible) { 1253 mNeedMenuInvalidate = true; 1254 } 1255 moveToState(fragment, mCurState, transition, transitionStyle, false); 1256 } 1257 } 1258 } 1259 1260 public Fragment findFragmentById(int id) { 1261 if (mAdded != null) { 1262 // First look through added fragments. 1263 for (int i=mAdded.size()-1; i>=0; i--) { 1264 Fragment f = mAdded.get(i); 1265 if (f != null && f.mFragmentId == id) { 1266 return f; 1267 } 1268 } 1269 } 1270 if (mActive != null) { 1271 // Now for any known fragment. 1272 for (int i=mActive.size()-1; i>=0; i--) { 1273 Fragment f = mActive.get(i); 1274 if (f != null && f.mFragmentId == id) { 1275 return f; 1276 } 1277 } 1278 } 1279 return null; 1280 } 1281 1282 public Fragment findFragmentByTag(String tag) { 1283 if (mAdded != null && tag != null) { 1284 // First look through added fragments. 1285 for (int i=mAdded.size()-1; i>=0; i--) { 1286 Fragment f = mAdded.get(i); 1287 if (f != null && tag.equals(f.mTag)) { 1288 return f; 1289 } 1290 } 1291 } 1292 if (mActive != null && tag != null) { 1293 // Now for any known fragment. 1294 for (int i=mActive.size()-1; i>=0; i--) { 1295 Fragment f = mActive.get(i); 1296 if (f != null && tag.equals(f.mTag)) { 1297 return f; 1298 } 1299 } 1300 } 1301 return null; 1302 } 1303 1304 public Fragment findFragmentByWho(String who) { 1305 if (mActive != null && who != null) { 1306 for (int i=mActive.size()-1; i>=0; i--) { 1307 Fragment f = mActive.get(i); 1308 if (f != null && (f=f.findFragmentByWho(who)) != null) { 1309 return f; 1310 } 1311 } 1312 } 1313 return null; 1314 } 1315 1316 private void checkStateLoss() { 1317 if (mStateSaved) { 1318 throw new IllegalStateException( 1319 "Can not perform this action after onSaveInstanceState"); 1320 } 1321 if (mNoTransactionsBecause != null) { 1322 throw new IllegalStateException( 1323 "Can not perform this action inside of " + mNoTransactionsBecause); 1324 } 1325 } 1326 1327 public void enqueueAction(Runnable action, boolean allowStateLoss) { 1328 if (!allowStateLoss) { 1329 checkStateLoss(); 1330 } 1331 synchronized (this) { 1332 if (mActivity == null) { 1333 throw new IllegalStateException("Activity has been destroyed"); 1334 } 1335 if (mPendingActions == null) { 1336 mPendingActions = new ArrayList<Runnable>(); 1337 } 1338 mPendingActions.add(action); 1339 if (mPendingActions.size() == 1) { 1340 mActivity.mHandler.removeCallbacks(mExecCommit); 1341 mActivity.mHandler.post(mExecCommit); 1342 } 1343 } 1344 } 1345 1346 public int allocBackStackIndex(BackStackRecord bse) { 1347 synchronized (this) { 1348 if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { 1349 if (mBackStackIndices == null) { 1350 mBackStackIndices = new ArrayList<BackStackRecord>(); 1351 } 1352 int index = mBackStackIndices.size(); 1353 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1354 mBackStackIndices.add(bse); 1355 return index; 1356 1357 } else { 1358 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); 1359 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1360 mBackStackIndices.set(index, bse); 1361 return index; 1362 } 1363 } 1364 } 1365 1366 public void setBackStackIndex(int index, BackStackRecord bse) { 1367 synchronized (this) { 1368 if (mBackStackIndices == null) { 1369 mBackStackIndices = new ArrayList<BackStackRecord>(); 1370 } 1371 int N = mBackStackIndices.size(); 1372 if (index < N) { 1373 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1374 mBackStackIndices.set(index, bse); 1375 } else { 1376 while (N < index) { 1377 mBackStackIndices.add(null); 1378 if (mAvailBackStackIndices == null) { 1379 mAvailBackStackIndices = new ArrayList<Integer>(); 1380 } 1381 if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); 1382 mAvailBackStackIndices.add(N); 1383 N++; 1384 } 1385 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1386 mBackStackIndices.add(bse); 1387 } 1388 } 1389 } 1390 1391 public void freeBackStackIndex(int index) { 1392 synchronized (this) { 1393 mBackStackIndices.set(index, null); 1394 if (mAvailBackStackIndices == null) { 1395 mAvailBackStackIndices = new ArrayList<Integer>(); 1396 } 1397 if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); 1398 mAvailBackStackIndices.add(index); 1399 } 1400 } 1401 1402 /** 1403 * Only call from main thread! 1404 */ 1405 public boolean execPendingActions() { 1406 if (mExecutingActions) { 1407 throw new IllegalStateException("Recursive entry to executePendingTransactions"); 1408 } 1409 1410 if (Looper.myLooper() != mActivity.mHandler.getLooper()) { 1411 throw new IllegalStateException("Must be called from main thread of process"); 1412 } 1413 1414 boolean didSomething = false; 1415 1416 while (true) { 1417 int numActions; 1418 1419 synchronized (this) { 1420 if (mPendingActions == null || mPendingActions.size() == 0) { 1421 break; 1422 } 1423 1424 numActions = mPendingActions.size(); 1425 if (mTmpActions == null || mTmpActions.length < numActions) { 1426 mTmpActions = new Runnable[numActions]; 1427 } 1428 mPendingActions.toArray(mTmpActions); 1429 mPendingActions.clear(); 1430 mActivity.mHandler.removeCallbacks(mExecCommit); 1431 } 1432 1433 mExecutingActions = true; 1434 for (int i=0; i<numActions; i++) { 1435 mTmpActions[i].run(); 1436 mTmpActions[i] = null; 1437 } 1438 mExecutingActions = false; 1439 didSomething = true; 1440 } 1441 1442 if (mHavePendingDeferredStart) { 1443 boolean loadersRunning = false; 1444 for (int i=0; i<mActive.size(); i++) { 1445 Fragment f = mActive.get(i); 1446 if (f != null && f.mLoaderManager != null) { 1447 loadersRunning |= f.mLoaderManager.hasRunningLoaders(); 1448 } 1449 } 1450 if (!loadersRunning) { 1451 mHavePendingDeferredStart = false; 1452 startPendingDeferredFragments(); 1453 } 1454 } 1455 return didSomething; 1456 } 1457 1458 void reportBackStackChanged() { 1459 if (mBackStackChangeListeners != null) { 1460 for (int i=0; i<mBackStackChangeListeners.size(); i++) { 1461 mBackStackChangeListeners.get(i).onBackStackChanged(); 1462 } 1463 } 1464 } 1465 1466 void addBackStackState(BackStackRecord state) { 1467 if (mBackStack == null) { 1468 mBackStack = new ArrayList<BackStackRecord>(); 1469 } 1470 mBackStack.add(state); 1471 reportBackStackChanged(); 1472 } 1473 1474 boolean popBackStackState(Handler handler, String name, int id, int flags) { 1475 if (mBackStack == null) { 1476 return false; 1477 } 1478 if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) { 1479 int last = mBackStack.size()-1; 1480 if (last < 0) { 1481 return false; 1482 } 1483 final BackStackRecord bss = mBackStack.remove(last); 1484 bss.popFromBackStack(true); 1485 reportBackStackChanged(); 1486 } else { 1487 int index = -1; 1488 if (name != null || id >= 0) { 1489 // If a name or ID is specified, look for that place in 1490 // the stack. 1491 index = mBackStack.size()-1; 1492 while (index >= 0) { 1493 BackStackRecord bss = mBackStack.get(index); 1494 if (name != null && name.equals(bss.getName())) { 1495 break; 1496 } 1497 if (id >= 0 && id == bss.mIndex) { 1498 break; 1499 } 1500 index--; 1501 } 1502 if (index < 0) { 1503 return false; 1504 } 1505 if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { 1506 index--; 1507 // Consume all following entries that match. 1508 while (index >= 0) { 1509 BackStackRecord bss = mBackStack.get(index); 1510 if ((name != null && name.equals(bss.getName())) 1511 || (id >= 0 && id == bss.mIndex)) { 1512 index--; 1513 continue; 1514 } 1515 break; 1516 } 1517 } 1518 } 1519 if (index == mBackStack.size()-1) { 1520 return false; 1521 } 1522 final ArrayList<BackStackRecord> states 1523 = new ArrayList<BackStackRecord>(); 1524 for (int i=mBackStack.size()-1; i>index; i--) { 1525 states.add(mBackStack.remove(i)); 1526 } 1527 final int LAST = states.size()-1; 1528 for (int i=0; i<=LAST; i++) { 1529 if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); 1530 states.get(i).popFromBackStack(i == LAST); 1531 } 1532 reportBackStackChanged(); 1533 } 1534 return true; 1535 } 1536 1537 ArrayList<Fragment> retainNonConfig() { 1538 ArrayList<Fragment> fragments = null; 1539 if (mActive != null) { 1540 for (int i=0; i<mActive.size(); i++) { 1541 Fragment f = mActive.get(i); 1542 if (f != null && f.mRetainInstance) { 1543 if (fragments == null) { 1544 fragments = new ArrayList<Fragment>(); 1545 } 1546 fragments.add(f); 1547 f.mRetaining = true; 1548 f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; 1549 if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f); 1550 } 1551 } 1552 } 1553 return fragments; 1554 } 1555 1556 void saveFragmentViewState(Fragment f) { 1557 if (f.mView == null) { 1558 return; 1559 } 1560 if (mStateArray == null) { 1561 mStateArray = new SparseArray<Parcelable>(); 1562 } else { 1563 mStateArray.clear(); 1564 } 1565 f.mView.saveHierarchyState(mStateArray); 1566 if (mStateArray.size() > 0) { 1567 f.mSavedViewState = mStateArray; 1568 mStateArray = null; 1569 } 1570 } 1571 1572 Bundle saveFragmentBasicState(Fragment f) { 1573 Bundle result = null; 1574 1575 if (mStateBundle == null) { 1576 mStateBundle = new Bundle(); 1577 } 1578 f.performSaveInstanceState(mStateBundle); 1579 if (!mStateBundle.isEmpty()) { 1580 result = mStateBundle; 1581 mStateBundle = null; 1582 } 1583 1584 if (f.mView != null) { 1585 saveFragmentViewState(f); 1586 } 1587 if (f.mSavedViewState != null) { 1588 if (result == null) { 1589 result = new Bundle(); 1590 } 1591 result.putSparseParcelableArray( 1592 FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); 1593 } 1594 if (!f.mUserVisibleHint) { 1595 if (result == null) { 1596 result = new Bundle(); 1597 } 1598 // Only add this if it's not the default value 1599 result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint); 1600 } 1601 1602 return result; 1603 } 1604 1605 Parcelable saveAllState() { 1606 // Make sure all pending operations have now been executed to get 1607 // our state update-to-date. 1608 execPendingActions(); 1609 1610 mStateSaved = true; 1611 1612 if (mActive == null || mActive.size() <= 0) { 1613 return null; 1614 } 1615 1616 // First collect all active fragments. 1617 int N = mActive.size(); 1618 FragmentState[] active = new FragmentState[N]; 1619 boolean haveFragments = false; 1620 for (int i=0; i<N; i++) { 1621 Fragment f = mActive.get(i); 1622 if (f != null) { 1623 if (f.mIndex < 0) { 1624 throwException(new IllegalStateException( 1625 "Failure saving state: active " + f 1626 + " has cleared index: " + f.mIndex)); 1627 } 1628 1629 haveFragments = true; 1630 1631 FragmentState fs = new FragmentState(f); 1632 active[i] = fs; 1633 1634 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { 1635 fs.mSavedFragmentState = saveFragmentBasicState(f); 1636 1637 if (f.mTarget != null) { 1638 if (f.mTarget.mIndex < 0) { 1639 throwException(new IllegalStateException( 1640 "Failure saving state: " + f 1641 + " has target not in fragment manager: " + f.mTarget)); 1642 } 1643 if (fs.mSavedFragmentState == null) { 1644 fs.mSavedFragmentState = new Bundle(); 1645 } 1646 putFragment(fs.mSavedFragmentState, 1647 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); 1648 if (f.mTargetRequestCode != 0) { 1649 fs.mSavedFragmentState.putInt( 1650 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 1651 f.mTargetRequestCode); 1652 } 1653 } 1654 1655 } else { 1656 fs.mSavedFragmentState = f.mSavedFragmentState; 1657 } 1658 1659 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " 1660 + fs.mSavedFragmentState); 1661 } 1662 } 1663 1664 if (!haveFragments) { 1665 if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); 1666 return null; 1667 } 1668 1669 int[] added = null; 1670 BackStackState[] backStack = null; 1671 1672 // Build list of currently added fragments. 1673 if (mAdded != null) { 1674 N = mAdded.size(); 1675 if (N > 0) { 1676 added = new int[N]; 1677 for (int i=0; i<N; i++) { 1678 added[i] = mAdded.get(i).mIndex; 1679 if (added[i] < 0) { 1680 throwException(new IllegalStateException( 1681 "Failure saving state: active " + mAdded.get(i) 1682 + " has cleared index: " + added[i])); 1683 } 1684 if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i 1685 + ": " + mAdded.get(i)); 1686 } 1687 } 1688 } 1689 1690 // Now save back stack. 1691 if (mBackStack != null) { 1692 N = mBackStack.size(); 1693 if (N > 0) { 1694 backStack = new BackStackState[N]; 1695 for (int i=0; i<N; i++) { 1696 backStack[i] = new BackStackState(this, mBackStack.get(i)); 1697 if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i 1698 + ": " + mBackStack.get(i)); 1699 } 1700 } 1701 } 1702 1703 FragmentManagerState fms = new FragmentManagerState(); 1704 fms.mActive = active; 1705 fms.mAdded = added; 1706 fms.mBackStack = backStack; 1707 return fms; 1708 } 1709 1710 void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) { 1711 // If there is no saved state at all, then there can not be 1712 // any nonConfig fragments either, so that is that. 1713 if (state == null) return; 1714 FragmentManagerState fms = (FragmentManagerState)state; 1715 if (fms.mActive == null) return; 1716 1717 // First re-attach any non-config instances we are retaining back 1718 // to their saved state, so we don't try to instantiate them again. 1719 if (nonConfig != null) { 1720 for (int i=0; i<nonConfig.size(); i++) { 1721 Fragment f = nonConfig.get(i); 1722 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f); 1723 FragmentState fs = fms.mActive[f.mIndex]; 1724 fs.mInstance = f; 1725 f.mSavedViewState = null; 1726 f.mBackStackNesting = 0; 1727 f.mInLayout = false; 1728 f.mAdded = false; 1729 f.mTarget = null; 1730 if (fs.mSavedFragmentState != null) { 1731 fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader()); 1732 f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( 1733 FragmentManagerImpl.VIEW_STATE_TAG); 1734 } 1735 } 1736 } 1737 1738 // Build the full list of active fragments, instantiating them from 1739 // their saved state. 1740 mActive = new ArrayList<Fragment>(fms.mActive.length); 1741 if (mAvailIndices != null) { 1742 mAvailIndices.clear(); 1743 } 1744 for (int i=0; i<fms.mActive.length; i++) { 1745 FragmentState fs = fms.mActive[i]; 1746 if (fs != null) { 1747 Fragment f = fs.instantiate(mActivity, mParent); 1748 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f); 1749 mActive.add(f); 1750 // Now that the fragment is instantiated (or came from being 1751 // retained above), clear mInstance in case we end up re-restoring 1752 // from this FragmentState again. 1753 fs.mInstance = null; 1754 } else { 1755 mActive.add(null); 1756 if (mAvailIndices == null) { 1757 mAvailIndices = new ArrayList<Integer>(); 1758 } 1759 if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i); 1760 mAvailIndices.add(i); 1761 } 1762 } 1763 1764 // Update the target of all retained fragments. 1765 if (nonConfig != null) { 1766 for (int i=0; i<nonConfig.size(); i++) { 1767 Fragment f = nonConfig.get(i); 1768 if (f.mTargetIndex >= 0) { 1769 if (f.mTargetIndex < mActive.size()) { 1770 f.mTarget = mActive.get(f.mTargetIndex); 1771 } else { 1772 Log.w(TAG, "Re-attaching retained fragment " + f 1773 + " target no longer exists: " + f.mTargetIndex); 1774 f.mTarget = null; 1775 } 1776 } 1777 } 1778 } 1779 1780 // Build the list of currently added fragments. 1781 if (fms.mAdded != null) { 1782 mAdded = new ArrayList<Fragment>(fms.mAdded.length); 1783 for (int i=0; i<fms.mAdded.length; i++) { 1784 Fragment f = mActive.get(fms.mAdded[i]); 1785 if (f == null) { 1786 throwException(new IllegalStateException( 1787 "No instantiated fragment for index #" + fms.mAdded[i])); 1788 } 1789 f.mAdded = true; 1790 if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f); 1791 if (mAdded.contains(f)) { 1792 throw new IllegalStateException("Already added!"); 1793 } 1794 mAdded.add(f); 1795 } 1796 } else { 1797 mAdded = null; 1798 } 1799 1800 // Build the back stack. 1801 if (fms.mBackStack != null) { 1802 mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); 1803 for (int i=0; i<fms.mBackStack.length; i++) { 1804 BackStackRecord bse = fms.mBackStack[i].instantiate(this); 1805 if (DEBUG) { 1806 Log.v(TAG, "restoreAllState: back stack #" + i 1807 + " (index " + bse.mIndex + "): " + bse); 1808 LogWriter logw = new LogWriter(Log.VERBOSE, TAG); 1809 PrintWriter pw = new PrintWriter(logw); 1810 bse.dump(" ", pw, false); 1811 } 1812 mBackStack.add(bse); 1813 if (bse.mIndex >= 0) { 1814 setBackStackIndex(bse.mIndex, bse); 1815 } 1816 } 1817 } else { 1818 mBackStack = null; 1819 } 1820 } 1821 1822 public void attachActivity(Activity activity, FragmentContainer container, Fragment parent) { 1823 if (mActivity != null) throw new IllegalStateException("Already attached"); 1824 mActivity = activity; 1825 mContainer = container; 1826 mParent = parent; 1827 } 1828 1829 public void noteStateNotSaved() { 1830 mStateSaved = false; 1831 } 1832 1833 public void dispatchCreate() { 1834 mStateSaved = false; 1835 moveToState(Fragment.CREATED, false); 1836 } 1837 1838 public void dispatchActivityCreated() { 1839 mStateSaved = false; 1840 moveToState(Fragment.ACTIVITY_CREATED, false); 1841 } 1842 1843 public void dispatchStart() { 1844 mStateSaved = false; 1845 moveToState(Fragment.STARTED, false); 1846 } 1847 1848 public void dispatchResume() { 1849 mStateSaved = false; 1850 moveToState(Fragment.RESUMED, false); 1851 } 1852 1853 public void dispatchPause() { 1854 moveToState(Fragment.STARTED, false); 1855 } 1856 1857 public void dispatchStop() { 1858 moveToState(Fragment.STOPPED, false); 1859 } 1860 1861 public void dispatchDestroyView() { 1862 moveToState(Fragment.CREATED, false); 1863 } 1864 1865 public void dispatchDestroy() { 1866 mDestroyed = true; 1867 execPendingActions(); 1868 moveToState(Fragment.INITIALIZING, false); 1869 mActivity = null; 1870 mContainer = null; 1871 mParent = null; 1872 } 1873 1874 public void dispatchConfigurationChanged(Configuration newConfig) { 1875 if (mAdded != null) { 1876 for (int i=0; i<mAdded.size(); i++) { 1877 Fragment f = mAdded.get(i); 1878 if (f != null) { 1879 f.performConfigurationChanged(newConfig); 1880 } 1881 } 1882 } 1883 } 1884 1885 public void dispatchLowMemory() { 1886 if (mAdded != null) { 1887 for (int i=0; i<mAdded.size(); i++) { 1888 Fragment f = mAdded.get(i); 1889 if (f != null) { 1890 f.performLowMemory(); 1891 } 1892 } 1893 } 1894 } 1895 1896 public void dispatchTrimMemory(int level) { 1897 if (mAdded != null) { 1898 for (int i=0; i<mAdded.size(); i++) { 1899 Fragment f = mAdded.get(i); 1900 if (f != null) { 1901 f.performTrimMemory(level); 1902 } 1903 } 1904 } 1905 } 1906 1907 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 1908 boolean show = false; 1909 ArrayList<Fragment> newMenus = null; 1910 if (mAdded != null) { 1911 for (int i=0; i<mAdded.size(); i++) { 1912 Fragment f = mAdded.get(i); 1913 if (f != null) { 1914 if (f.performCreateOptionsMenu(menu, inflater)) { 1915 show = true; 1916 if (newMenus == null) { 1917 newMenus = new ArrayList<Fragment>(); 1918 } 1919 newMenus.add(f); 1920 } 1921 } 1922 } 1923 } 1924 1925 if (mCreatedMenus != null) { 1926 for (int i=0; i<mCreatedMenus.size(); i++) { 1927 Fragment f = mCreatedMenus.get(i); 1928 if (newMenus == null || !newMenus.contains(f)) { 1929 f.onDestroyOptionsMenu(); 1930 } 1931 } 1932 } 1933 1934 mCreatedMenus = newMenus; 1935 1936 return show; 1937 } 1938 1939 public boolean dispatchPrepareOptionsMenu(Menu menu) { 1940 boolean show = false; 1941 if (mAdded != null) { 1942 for (int i=0; i<mAdded.size(); i++) { 1943 Fragment f = mAdded.get(i); 1944 if (f != null) { 1945 if (f.performPrepareOptionsMenu(menu)) { 1946 show = true; 1947 } 1948 } 1949 } 1950 } 1951 return show; 1952 } 1953 1954 public boolean dispatchOptionsItemSelected(MenuItem item) { 1955 if (mAdded != null) { 1956 for (int i=0; i<mAdded.size(); i++) { 1957 Fragment f = mAdded.get(i); 1958 if (f != null) { 1959 if (f.performOptionsItemSelected(item)) { 1960 return true; 1961 } 1962 } 1963 } 1964 } 1965 return false; 1966 } 1967 1968 public boolean dispatchContextItemSelected(MenuItem item) { 1969 if (mAdded != null) { 1970 for (int i=0; i<mAdded.size(); i++) { 1971 Fragment f = mAdded.get(i); 1972 if (f != null) { 1973 if (f.performContextItemSelected(item)) { 1974 return true; 1975 } 1976 } 1977 } 1978 } 1979 return false; 1980 } 1981 1982 public void dispatchOptionsMenuClosed(Menu menu) { 1983 if (mAdded != null) { 1984 for (int i=0; i<mAdded.size(); i++) { 1985 Fragment f = mAdded.get(i); 1986 if (f != null) { 1987 f.performOptionsMenuClosed(menu); 1988 } 1989 } 1990 } 1991 } 1992 1993 @Override 1994 public void invalidateOptionsMenu() { 1995 if (mActivity != null && mCurState == Fragment.RESUMED) { 1996 mActivity.invalidateOptionsMenu(); 1997 } else { 1998 mNeedMenuInvalidate = true; 1999 } 2000 } 2001 2002 public static int reverseTransit(int transit) { 2003 int rev = 0; 2004 switch (transit) { 2005 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 2006 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; 2007 break; 2008 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 2009 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; 2010 break; 2011 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 2012 rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE; 2013 break; 2014 } 2015 return rev; 2016 2017 } 2018 2019 public static int transitToStyleIndex(int transit, boolean enter) { 2020 int animAttr = -1; 2021 switch (transit) { 2022 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 2023 animAttr = enter 2024 ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation 2025 : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation; 2026 break; 2027 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 2028 animAttr = enter 2029 ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation 2030 : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation; 2031 break; 2032 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 2033 animAttr = enter 2034 ? com.android.internal.R.styleable.FragmentAnimation_fragmentFadeEnterAnimation 2035 : com.android.internal.R.styleable.FragmentAnimation_fragmentFadeExitAnimation; 2036 break; 2037 } 2038 return animAttr; 2039 } 2040 } 2041