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