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.util.SuperNotCalledException; 35 import android.view.Menu; 36 import android.view.MenuInflater; 37 import android.view.MenuItem; 38 import android.view.View; 39 import android.view.ViewGroup; 40 import com.android.internal.util.FastPrintWriter; 41 42 import java.io.FileDescriptor; 43 import java.io.PrintWriter; 44 import java.util.ArrayList; 45 import 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 */ 64 public 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 354 final 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 */ 393 interface FragmentContainer { 394 public View findViewById(int id); 395 } 396 397 /** 398 * Container for fragments associated with an activity. 399 */ 400 final 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("Fragement no longer exists for key " 578 + key + ": index " + index)); 579 } 580 Fragment f = mActive.get(index); 581 if (f == null) { 582 throwException(new IllegalStateException("Fragement 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 } 1030 } 1031 } 1032 } 1033 } 1034 } 1035 1036 f.mState = newState; 1037 } 1038 1039 void moveToState(Fragment f) { 1040 moveToState(f, mCurState, 0, 0, false); 1041 } 1042 1043 void moveToState(int newState, boolean always) { 1044 moveToState(newState, 0, 0, always); 1045 } 1046 1047 void moveToState(int newState, int transit, int transitStyle, boolean always) { 1048 if (mActivity == null && newState != Fragment.INITIALIZING) { 1049 throw new IllegalStateException("No activity"); 1050 } 1051 1052 if (!always && mCurState == newState) { 1053 return; 1054 } 1055 1056 mCurState = newState; 1057 if (mActive != null) { 1058 boolean loadersRunning = false; 1059 for (int i=0; i<mActive.size(); i++) { 1060 Fragment f = mActive.get(i); 1061 if (f != null) { 1062 moveToState(f, newState, transit, transitStyle, false); 1063 if (f.mLoaderManager != null) { 1064 loadersRunning |= f.mLoaderManager.hasRunningLoaders(); 1065 } 1066 } 1067 } 1068 1069 if (!loadersRunning) { 1070 startPendingDeferredFragments(); 1071 } 1072 1073 if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) { 1074 mActivity.invalidateOptionsMenu(); 1075 mNeedMenuInvalidate = false; 1076 } 1077 } 1078 } 1079 1080 void startPendingDeferredFragments() { 1081 if (mActive == null) return; 1082 1083 for (int i=0; i<mActive.size(); i++) { 1084 Fragment f = mActive.get(i); 1085 if (f != null) { 1086 performPendingDeferredStart(f); 1087 } 1088 } 1089 } 1090 1091 void makeActive(Fragment f) { 1092 if (f.mIndex >= 0) { 1093 return; 1094 } 1095 1096 if (mAvailIndices == null || mAvailIndices.size() <= 0) { 1097 if (mActive == null) { 1098 mActive = new ArrayList<Fragment>(); 1099 } 1100 f.setIndex(mActive.size(), mParent); 1101 mActive.add(f); 1102 1103 } else { 1104 f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent); 1105 mActive.set(f.mIndex, f); 1106 } 1107 if (DEBUG) Log.v(TAG, "Allocated fragment index " + f); 1108 } 1109 1110 void makeInactive(Fragment f) { 1111 if (f.mIndex < 0) { 1112 return; 1113 } 1114 1115 if (DEBUG) Log.v(TAG, "Freeing fragment index " + f); 1116 mActive.set(f.mIndex, null); 1117 if (mAvailIndices == null) { 1118 mAvailIndices = new ArrayList<Integer>(); 1119 } 1120 mAvailIndices.add(f.mIndex); 1121 mActivity.invalidateFragment(f.mWho); 1122 f.initState(); 1123 } 1124 1125 public void addFragment(Fragment fragment, boolean moveToStateNow) { 1126 if (mAdded == null) { 1127 mAdded = new ArrayList<Fragment>(); 1128 } 1129 if (DEBUG) Log.v(TAG, "add: " + fragment); 1130 makeActive(fragment); 1131 if (!fragment.mDetached) { 1132 if (mAdded.contains(fragment)) { 1133 throw new IllegalStateException("Fragment already added: " + fragment); 1134 } 1135 mAdded.add(fragment); 1136 fragment.mAdded = true; 1137 fragment.mRemoving = false; 1138 if (fragment.mHasMenu && fragment.mMenuVisible) { 1139 mNeedMenuInvalidate = true; 1140 } 1141 if (moveToStateNow) { 1142 moveToState(fragment); 1143 } 1144 } 1145 } 1146 1147 public void removeFragment(Fragment fragment, int transition, int transitionStyle) { 1148 if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); 1149 final boolean inactive = !fragment.isInBackStack(); 1150 if (!fragment.mDetached || inactive) { 1151 if (false) { 1152 // Would be nice to catch a bad remove here, but we need 1153 // time to test this to make sure we aren't crashes cases 1154 // where it is not a problem. 1155 if (!mAdded.contains(fragment)) { 1156 throw new IllegalStateException("Fragment not added: " + fragment); 1157 } 1158 } 1159 if (mAdded != null) { 1160 mAdded.remove(fragment); 1161 } 1162 if (fragment.mHasMenu && fragment.mMenuVisible) { 1163 mNeedMenuInvalidate = true; 1164 } 1165 fragment.mAdded = false; 1166 fragment.mRemoving = true; 1167 moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, 1168 transition, transitionStyle, false); 1169 } 1170 } 1171 1172 public void hideFragment(Fragment fragment, int transition, int transitionStyle) { 1173 if (DEBUG) Log.v(TAG, "hide: " + fragment); 1174 if (!fragment.mHidden) { 1175 fragment.mHidden = true; 1176 if (fragment.mView != null) { 1177 Animator anim = loadAnimator(fragment, transition, false, 1178 transitionStyle); 1179 if (anim != null) { 1180 anim.setTarget(fragment.mView); 1181 // Delay the actual hide operation until the animation finishes, otherwise 1182 // the fragment will just immediately disappear 1183 final Fragment finalFragment = fragment; 1184 anim.addListener(new AnimatorListenerAdapter() { 1185 @Override 1186 public void onAnimationEnd(Animator animation) { 1187 if (finalFragment.mView != null) { 1188 finalFragment.mView.setVisibility(View.GONE); 1189 } 1190 } 1191 }); 1192 anim.start(); 1193 } else { 1194 fragment.mView.setVisibility(View.GONE); 1195 } 1196 } 1197 if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { 1198 mNeedMenuInvalidate = true; 1199 } 1200 fragment.onHiddenChanged(true); 1201 } 1202 } 1203 1204 public void showFragment(Fragment fragment, int transition, int transitionStyle) { 1205 if (DEBUG) Log.v(TAG, "show: " + fragment); 1206 if (fragment.mHidden) { 1207 fragment.mHidden = false; 1208 if (fragment.mView != null) { 1209 Animator anim = loadAnimator(fragment, transition, true, 1210 transitionStyle); 1211 if (anim != null) { 1212 anim.setTarget(fragment.mView); 1213 anim.start(); 1214 } 1215 fragment.mView.setVisibility(View.VISIBLE); 1216 } 1217 if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { 1218 mNeedMenuInvalidate = true; 1219 } 1220 fragment.onHiddenChanged(false); 1221 } 1222 } 1223 1224 public void detachFragment(Fragment fragment, int transition, int transitionStyle) { 1225 if (DEBUG) Log.v(TAG, "detach: " + fragment); 1226 if (!fragment.mDetached) { 1227 fragment.mDetached = true; 1228 if (fragment.mAdded) { 1229 // We are not already in back stack, so need to remove the fragment. 1230 if (mAdded != null) { 1231 if (DEBUG) Log.v(TAG, "remove from detach: " + fragment); 1232 mAdded.remove(fragment); 1233 } 1234 if (fragment.mHasMenu && fragment.mMenuVisible) { 1235 mNeedMenuInvalidate = true; 1236 } 1237 fragment.mAdded = false; 1238 moveToState(fragment, Fragment.CREATED, transition, transitionStyle, false); 1239 } 1240 } 1241 } 1242 1243 public void attachFragment(Fragment fragment, int transition, int transitionStyle) { 1244 if (DEBUG) Log.v(TAG, "attach: " + fragment); 1245 if (fragment.mDetached) { 1246 fragment.mDetached = false; 1247 if (!fragment.mAdded) { 1248 if (mAdded == null) { 1249 mAdded = new ArrayList<Fragment>(); 1250 } 1251 if (mAdded.contains(fragment)) { 1252 throw new IllegalStateException("Fragment already added: " + fragment); 1253 } 1254 if (DEBUG) Log.v(TAG, "add from attach: " + fragment); 1255 mAdded.add(fragment); 1256 fragment.mAdded = true; 1257 if (fragment.mHasMenu && fragment.mMenuVisible) { 1258 mNeedMenuInvalidate = true; 1259 } 1260 moveToState(fragment, mCurState, transition, transitionStyle, false); 1261 } 1262 } 1263 } 1264 1265 public Fragment findFragmentById(int id) { 1266 if (mAdded != null) { 1267 // First look through added fragments. 1268 for (int i=mAdded.size()-1; i>=0; i--) { 1269 Fragment f = mAdded.get(i); 1270 if (f != null && f.mFragmentId == id) { 1271 return f; 1272 } 1273 } 1274 } 1275 if (mActive != null) { 1276 // Now for any known fragment. 1277 for (int i=mActive.size()-1; i>=0; i--) { 1278 Fragment f = mActive.get(i); 1279 if (f != null && f.mFragmentId == id) { 1280 return f; 1281 } 1282 } 1283 } 1284 return null; 1285 } 1286 1287 public Fragment findFragmentByTag(String tag) { 1288 if (mAdded != null && tag != null) { 1289 // First look through added fragments. 1290 for (int i=mAdded.size()-1; i>=0; i--) { 1291 Fragment f = mAdded.get(i); 1292 if (f != null && tag.equals(f.mTag)) { 1293 return f; 1294 } 1295 } 1296 } 1297 if (mActive != null && tag != null) { 1298 // Now for any known fragment. 1299 for (int i=mActive.size()-1; i>=0; i--) { 1300 Fragment f = mActive.get(i); 1301 if (f != null && tag.equals(f.mTag)) { 1302 return f; 1303 } 1304 } 1305 } 1306 return null; 1307 } 1308 1309 public Fragment findFragmentByWho(String who) { 1310 if (mActive != null && who != null) { 1311 for (int i=mActive.size()-1; i>=0; i--) { 1312 Fragment f = mActive.get(i); 1313 if (f != null && (f=f.findFragmentByWho(who)) != null) { 1314 return f; 1315 } 1316 } 1317 } 1318 return null; 1319 } 1320 1321 private void checkStateLoss() { 1322 if (mStateSaved) { 1323 throw new IllegalStateException( 1324 "Can not perform this action after onSaveInstanceState"); 1325 } 1326 if (mNoTransactionsBecause != null) { 1327 throw new IllegalStateException( 1328 "Can not perform this action inside of " + mNoTransactionsBecause); 1329 } 1330 } 1331 1332 /** 1333 * Adds an action to the queue of pending actions. 1334 * 1335 * @param action the action to add 1336 * @param allowStateLoss whether to allow loss of state information 1337 * @throws IllegalStateException if the activity has been destroyed 1338 */ 1339 public void enqueueAction(Runnable action, boolean allowStateLoss) { 1340 if (!allowStateLoss) { 1341 checkStateLoss(); 1342 } 1343 synchronized (this) { 1344 if (mDestroyed || mActivity == null) { 1345 throw new IllegalStateException("Activity has been destroyed"); 1346 } 1347 if (mPendingActions == null) { 1348 mPendingActions = new ArrayList<Runnable>(); 1349 } 1350 mPendingActions.add(action); 1351 if (mPendingActions.size() == 1) { 1352 mActivity.mHandler.removeCallbacks(mExecCommit); 1353 mActivity.mHandler.post(mExecCommit); 1354 } 1355 } 1356 } 1357 1358 public int allocBackStackIndex(BackStackRecord bse) { 1359 synchronized (this) { 1360 if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { 1361 if (mBackStackIndices == null) { 1362 mBackStackIndices = new ArrayList<BackStackRecord>(); 1363 } 1364 int index = mBackStackIndices.size(); 1365 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1366 mBackStackIndices.add(bse); 1367 return index; 1368 1369 } else { 1370 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); 1371 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1372 mBackStackIndices.set(index, bse); 1373 return index; 1374 } 1375 } 1376 } 1377 1378 public void setBackStackIndex(int index, BackStackRecord bse) { 1379 synchronized (this) { 1380 if (mBackStackIndices == null) { 1381 mBackStackIndices = new ArrayList<BackStackRecord>(); 1382 } 1383 int N = mBackStackIndices.size(); 1384 if (index < N) { 1385 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1386 mBackStackIndices.set(index, bse); 1387 } else { 1388 while (N < index) { 1389 mBackStackIndices.add(null); 1390 if (mAvailBackStackIndices == null) { 1391 mAvailBackStackIndices = new ArrayList<Integer>(); 1392 } 1393 if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); 1394 mAvailBackStackIndices.add(N); 1395 N++; 1396 } 1397 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1398 mBackStackIndices.add(bse); 1399 } 1400 } 1401 } 1402 1403 public void freeBackStackIndex(int index) { 1404 synchronized (this) { 1405 mBackStackIndices.set(index, null); 1406 if (mAvailBackStackIndices == null) { 1407 mAvailBackStackIndices = new ArrayList<Integer>(); 1408 } 1409 if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); 1410 mAvailBackStackIndices.add(index); 1411 } 1412 } 1413 1414 /** 1415 * Only call from main thread! 1416 */ 1417 public boolean execPendingActions() { 1418 if (mExecutingActions) { 1419 throw new IllegalStateException("Recursive entry to executePendingTransactions"); 1420 } 1421 1422 if (Looper.myLooper() != mActivity.mHandler.getLooper()) { 1423 throw new IllegalStateException("Must be called from main thread of process"); 1424 } 1425 1426 boolean didSomething = false; 1427 1428 while (true) { 1429 int numActions; 1430 1431 synchronized (this) { 1432 if (mPendingActions == null || mPendingActions.size() == 0) { 1433 break; 1434 } 1435 1436 numActions = mPendingActions.size(); 1437 if (mTmpActions == null || mTmpActions.length < numActions) { 1438 mTmpActions = new Runnable[numActions]; 1439 } 1440 mPendingActions.toArray(mTmpActions); 1441 mPendingActions.clear(); 1442 mActivity.mHandler.removeCallbacks(mExecCommit); 1443 } 1444 1445 mExecutingActions = true; 1446 for (int i=0; i<numActions; i++) { 1447 mTmpActions[i].run(); 1448 mTmpActions[i] = null; 1449 } 1450 mExecutingActions = false; 1451 didSomething = true; 1452 } 1453 1454 if (mHavePendingDeferredStart) { 1455 boolean loadersRunning = false; 1456 for (int i=0; i<mActive.size(); i++) { 1457 Fragment f = mActive.get(i); 1458 if (f != null && f.mLoaderManager != null) { 1459 loadersRunning |= f.mLoaderManager.hasRunningLoaders(); 1460 } 1461 } 1462 if (!loadersRunning) { 1463 mHavePendingDeferredStart = false; 1464 startPendingDeferredFragments(); 1465 } 1466 } 1467 return didSomething; 1468 } 1469 1470 void reportBackStackChanged() { 1471 if (mBackStackChangeListeners != null) { 1472 for (int i=0; i<mBackStackChangeListeners.size(); i++) { 1473 mBackStackChangeListeners.get(i).onBackStackChanged(); 1474 } 1475 } 1476 } 1477 1478 void addBackStackState(BackStackRecord state) { 1479 if (mBackStack == null) { 1480 mBackStack = new ArrayList<BackStackRecord>(); 1481 } 1482 mBackStack.add(state); 1483 reportBackStackChanged(); 1484 } 1485 1486 boolean popBackStackState(Handler handler, String name, int id, int flags) { 1487 if (mBackStack == null) { 1488 return false; 1489 } 1490 if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) { 1491 int last = mBackStack.size()-1; 1492 if (last < 0) { 1493 return false; 1494 } 1495 final BackStackRecord bss = mBackStack.remove(last); 1496 bss.popFromBackStack(true); 1497 reportBackStackChanged(); 1498 } else { 1499 int index = -1; 1500 if (name != null || id >= 0) { 1501 // If a name or ID is specified, look for that place in 1502 // the stack. 1503 index = mBackStack.size()-1; 1504 while (index >= 0) { 1505 BackStackRecord bss = mBackStack.get(index); 1506 if (name != null && name.equals(bss.getName())) { 1507 break; 1508 } 1509 if (id >= 0 && id == bss.mIndex) { 1510 break; 1511 } 1512 index--; 1513 } 1514 if (index < 0) { 1515 return false; 1516 } 1517 if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { 1518 index--; 1519 // Consume all following entries that match. 1520 while (index >= 0) { 1521 BackStackRecord bss = mBackStack.get(index); 1522 if ((name != null && name.equals(bss.getName())) 1523 || (id >= 0 && id == bss.mIndex)) { 1524 index--; 1525 continue; 1526 } 1527 break; 1528 } 1529 } 1530 } 1531 if (index == mBackStack.size()-1) { 1532 return false; 1533 } 1534 final ArrayList<BackStackRecord> states 1535 = new ArrayList<BackStackRecord>(); 1536 for (int i=mBackStack.size()-1; i>index; i--) { 1537 states.add(mBackStack.remove(i)); 1538 } 1539 final int LAST = states.size()-1; 1540 for (int i=0; i<=LAST; i++) { 1541 if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); 1542 states.get(i).popFromBackStack(i == LAST); 1543 } 1544 reportBackStackChanged(); 1545 } 1546 return true; 1547 } 1548 1549 ArrayList<Fragment> retainNonConfig() { 1550 ArrayList<Fragment> fragments = null; 1551 if (mActive != null) { 1552 for (int i=0; i<mActive.size(); i++) { 1553 Fragment f = mActive.get(i); 1554 if (f != null && f.mRetainInstance) { 1555 if (fragments == null) { 1556 fragments = new ArrayList<Fragment>(); 1557 } 1558 fragments.add(f); 1559 f.mRetaining = true; 1560 f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; 1561 if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f); 1562 } 1563 } 1564 } 1565 return fragments; 1566 } 1567 1568 void saveFragmentViewState(Fragment f) { 1569 if (f.mView == null) { 1570 return; 1571 } 1572 if (mStateArray == null) { 1573 mStateArray = new SparseArray<Parcelable>(); 1574 } else { 1575 mStateArray.clear(); 1576 } 1577 f.mView.saveHierarchyState(mStateArray); 1578 if (mStateArray.size() > 0) { 1579 f.mSavedViewState = mStateArray; 1580 mStateArray = null; 1581 } 1582 } 1583 1584 Bundle saveFragmentBasicState(Fragment f) { 1585 Bundle result = null; 1586 1587 if (mStateBundle == null) { 1588 mStateBundle = new Bundle(); 1589 } 1590 f.performSaveInstanceState(mStateBundle); 1591 if (!mStateBundle.isEmpty()) { 1592 result = mStateBundle; 1593 mStateBundle = null; 1594 } 1595 1596 if (f.mView != null) { 1597 saveFragmentViewState(f); 1598 } 1599 if (f.mSavedViewState != null) { 1600 if (result == null) { 1601 result = new Bundle(); 1602 } 1603 result.putSparseParcelableArray( 1604 FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); 1605 } 1606 if (!f.mUserVisibleHint) { 1607 if (result == null) { 1608 result = new Bundle(); 1609 } 1610 // Only add this if it's not the default value 1611 result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint); 1612 } 1613 1614 return result; 1615 } 1616 1617 Parcelable saveAllState() { 1618 // Make sure all pending operations have now been executed to get 1619 // our state update-to-date. 1620 execPendingActions(); 1621 1622 mStateSaved = true; 1623 1624 if (mActive == null || mActive.size() <= 0) { 1625 return null; 1626 } 1627 1628 // First collect all active fragments. 1629 int N = mActive.size(); 1630 FragmentState[] active = new FragmentState[N]; 1631 boolean haveFragments = false; 1632 for (int i=0; i<N; i++) { 1633 Fragment f = mActive.get(i); 1634 if (f != null) { 1635 if (f.mIndex < 0) { 1636 throwException(new IllegalStateException( 1637 "Failure saving state: active " + f 1638 + " has cleared index: " + f.mIndex)); 1639 } 1640 1641 haveFragments = true; 1642 1643 FragmentState fs = new FragmentState(f); 1644 active[i] = fs; 1645 1646 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { 1647 fs.mSavedFragmentState = saveFragmentBasicState(f); 1648 1649 if (f.mTarget != null) { 1650 if (f.mTarget.mIndex < 0) { 1651 throwException(new IllegalStateException( 1652 "Failure saving state: " + f 1653 + " has target not in fragment manager: " + f.mTarget)); 1654 } 1655 if (fs.mSavedFragmentState == null) { 1656 fs.mSavedFragmentState = new Bundle(); 1657 } 1658 putFragment(fs.mSavedFragmentState, 1659 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); 1660 if (f.mTargetRequestCode != 0) { 1661 fs.mSavedFragmentState.putInt( 1662 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 1663 f.mTargetRequestCode); 1664 } 1665 } 1666 1667 } else { 1668 fs.mSavedFragmentState = f.mSavedFragmentState; 1669 } 1670 1671 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " 1672 + fs.mSavedFragmentState); 1673 } 1674 } 1675 1676 if (!haveFragments) { 1677 if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); 1678 return null; 1679 } 1680 1681 int[] added = null; 1682 BackStackState[] backStack = null; 1683 1684 // Build list of currently added fragments. 1685 if (mAdded != null) { 1686 N = mAdded.size(); 1687 if (N > 0) { 1688 added = new int[N]; 1689 for (int i=0; i<N; i++) { 1690 added[i] = mAdded.get(i).mIndex; 1691 if (added[i] < 0) { 1692 throwException(new IllegalStateException( 1693 "Failure saving state: active " + mAdded.get(i) 1694 + " has cleared index: " + added[i])); 1695 } 1696 if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i 1697 + ": " + mAdded.get(i)); 1698 } 1699 } 1700 } 1701 1702 // Now save back stack. 1703 if (mBackStack != null) { 1704 N = mBackStack.size(); 1705 if (N > 0) { 1706 backStack = new BackStackState[N]; 1707 for (int i=0; i<N; i++) { 1708 backStack[i] = new BackStackState(this, mBackStack.get(i)); 1709 if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i 1710 + ": " + mBackStack.get(i)); 1711 } 1712 } 1713 } 1714 1715 FragmentManagerState fms = new FragmentManagerState(); 1716 fms.mActive = active; 1717 fms.mAdded = added; 1718 fms.mBackStack = backStack; 1719 return fms; 1720 } 1721 1722 void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) { 1723 // If there is no saved state at all, then there can not be 1724 // any nonConfig fragments either, so that is that. 1725 if (state == null) return; 1726 FragmentManagerState fms = (FragmentManagerState)state; 1727 if (fms.mActive == null) return; 1728 1729 // First re-attach any non-config instances we are retaining back 1730 // to their saved state, so we don't try to instantiate them again. 1731 if (nonConfig != null) { 1732 for (int i=0; i<nonConfig.size(); i++) { 1733 Fragment f = nonConfig.get(i); 1734 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f); 1735 FragmentState fs = fms.mActive[f.mIndex]; 1736 fs.mInstance = f; 1737 f.mSavedViewState = null; 1738 f.mBackStackNesting = 0; 1739 f.mInLayout = false; 1740 f.mAdded = false; 1741 f.mTarget = null; 1742 if (fs.mSavedFragmentState != null) { 1743 fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader()); 1744 f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( 1745 FragmentManagerImpl.VIEW_STATE_TAG); 1746 } 1747 } 1748 } 1749 1750 // Build the full list of active fragments, instantiating them from 1751 // their saved state. 1752 mActive = new ArrayList<Fragment>(fms.mActive.length); 1753 if (mAvailIndices != null) { 1754 mAvailIndices.clear(); 1755 } 1756 for (int i=0; i<fms.mActive.length; i++) { 1757 FragmentState fs = fms.mActive[i]; 1758 if (fs != null) { 1759 Fragment f = fs.instantiate(mActivity, mParent); 1760 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f); 1761 mActive.add(f); 1762 // Now that the fragment is instantiated (or came from being 1763 // retained above), clear mInstance in case we end up re-restoring 1764 // from this FragmentState again. 1765 fs.mInstance = null; 1766 } else { 1767 mActive.add(null); 1768 if (mAvailIndices == null) { 1769 mAvailIndices = new ArrayList<Integer>(); 1770 } 1771 if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i); 1772 mAvailIndices.add(i); 1773 } 1774 } 1775 1776 // Update the target of all retained fragments. 1777 if (nonConfig != null) { 1778 for (int i=0; i<nonConfig.size(); i++) { 1779 Fragment f = nonConfig.get(i); 1780 if (f.mTargetIndex >= 0) { 1781 if (f.mTargetIndex < mActive.size()) { 1782 f.mTarget = mActive.get(f.mTargetIndex); 1783 } else { 1784 Log.w(TAG, "Re-attaching retained fragment " + f 1785 + " target no longer exists: " + f.mTargetIndex); 1786 f.mTarget = null; 1787 } 1788 } 1789 } 1790 } 1791 1792 // Build the list of currently added fragments. 1793 if (fms.mAdded != null) { 1794 mAdded = new ArrayList<Fragment>(fms.mAdded.length); 1795 for (int i=0; i<fms.mAdded.length; i++) { 1796 Fragment f = mActive.get(fms.mAdded[i]); 1797 if (f == null) { 1798 throwException(new IllegalStateException( 1799 "No instantiated fragment for index #" + fms.mAdded[i])); 1800 } 1801 f.mAdded = true; 1802 if (DEBUG) Log.v(TAG, "restoreAllState: added #" + i + ": " + f); 1803 if (mAdded.contains(f)) { 1804 throw new IllegalStateException("Already added!"); 1805 } 1806 mAdded.add(f); 1807 } 1808 } else { 1809 mAdded = null; 1810 } 1811 1812 // Build the back stack. 1813 if (fms.mBackStack != null) { 1814 mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); 1815 for (int i=0; i<fms.mBackStack.length; i++) { 1816 BackStackRecord bse = fms.mBackStack[i].instantiate(this); 1817 if (DEBUG) { 1818 Log.v(TAG, "restoreAllState: back stack #" + i 1819 + " (index " + bse.mIndex + "): " + bse); 1820 LogWriter logw = new LogWriter(Log.VERBOSE, TAG); 1821 PrintWriter pw = new FastPrintWriter(logw, false, 1024); 1822 bse.dump(" ", pw, false); 1823 pw.flush(); 1824 } 1825 mBackStack.add(bse); 1826 if (bse.mIndex >= 0) { 1827 setBackStackIndex(bse.mIndex, bse); 1828 } 1829 } 1830 } else { 1831 mBackStack = null; 1832 } 1833 } 1834 1835 public void attachActivity(Activity activity, FragmentContainer container, Fragment parent) { 1836 if (mActivity != null) throw new IllegalStateException("Already attached"); 1837 mActivity = activity; 1838 mContainer = container; 1839 mParent = parent; 1840 } 1841 1842 public void noteStateNotSaved() { 1843 mStateSaved = false; 1844 } 1845 1846 public void dispatchCreate() { 1847 mStateSaved = false; 1848 moveToState(Fragment.CREATED, false); 1849 } 1850 1851 public void dispatchActivityCreated() { 1852 mStateSaved = false; 1853 moveToState(Fragment.ACTIVITY_CREATED, false); 1854 } 1855 1856 public void dispatchStart() { 1857 mStateSaved = false; 1858 moveToState(Fragment.STARTED, false); 1859 } 1860 1861 public void dispatchResume() { 1862 mStateSaved = false; 1863 moveToState(Fragment.RESUMED, false); 1864 } 1865 1866 public void dispatchPause() { 1867 moveToState(Fragment.STARTED, false); 1868 } 1869 1870 public void dispatchStop() { 1871 moveToState(Fragment.STOPPED, false); 1872 } 1873 1874 public void dispatchDestroyView() { 1875 moveToState(Fragment.CREATED, false); 1876 } 1877 1878 public void dispatchDestroy() { 1879 mDestroyed = true; 1880 execPendingActions(); 1881 moveToState(Fragment.INITIALIZING, false); 1882 mActivity = null; 1883 mContainer = null; 1884 mParent = null; 1885 } 1886 1887 public void dispatchConfigurationChanged(Configuration newConfig) { 1888 if (mAdded != null) { 1889 for (int i=0; i<mAdded.size(); i++) { 1890 Fragment f = mAdded.get(i); 1891 if (f != null) { 1892 f.performConfigurationChanged(newConfig); 1893 } 1894 } 1895 } 1896 } 1897 1898 public void dispatchLowMemory() { 1899 if (mAdded != null) { 1900 for (int i=0; i<mAdded.size(); i++) { 1901 Fragment f = mAdded.get(i); 1902 if (f != null) { 1903 f.performLowMemory(); 1904 } 1905 } 1906 } 1907 } 1908 1909 public void dispatchTrimMemory(int level) { 1910 if (mAdded != null) { 1911 for (int i=0; i<mAdded.size(); i++) { 1912 Fragment f = mAdded.get(i); 1913 if (f != null) { 1914 f.performTrimMemory(level); 1915 } 1916 } 1917 } 1918 } 1919 1920 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 1921 boolean show = false; 1922 ArrayList<Fragment> newMenus = null; 1923 if (mAdded != null) { 1924 for (int i=0; i<mAdded.size(); i++) { 1925 Fragment f = mAdded.get(i); 1926 if (f != null) { 1927 if (f.performCreateOptionsMenu(menu, inflater)) { 1928 show = true; 1929 if (newMenus == null) { 1930 newMenus = new ArrayList<Fragment>(); 1931 } 1932 newMenus.add(f); 1933 } 1934 } 1935 } 1936 } 1937 1938 if (mCreatedMenus != null) { 1939 for (int i=0; i<mCreatedMenus.size(); i++) { 1940 Fragment f = mCreatedMenus.get(i); 1941 if (newMenus == null || !newMenus.contains(f)) { 1942 f.onDestroyOptionsMenu(); 1943 } 1944 } 1945 } 1946 1947 mCreatedMenus = newMenus; 1948 1949 return show; 1950 } 1951 1952 public boolean dispatchPrepareOptionsMenu(Menu menu) { 1953 boolean show = false; 1954 if (mAdded != null) { 1955 for (int i=0; i<mAdded.size(); i++) { 1956 Fragment f = mAdded.get(i); 1957 if (f != null) { 1958 if (f.performPrepareOptionsMenu(menu)) { 1959 show = true; 1960 } 1961 } 1962 } 1963 } 1964 return show; 1965 } 1966 1967 public boolean dispatchOptionsItemSelected(MenuItem item) { 1968 if (mAdded != null) { 1969 for (int i=0; i<mAdded.size(); i++) { 1970 Fragment f = mAdded.get(i); 1971 if (f != null) { 1972 if (f.performOptionsItemSelected(item)) { 1973 return true; 1974 } 1975 } 1976 } 1977 } 1978 return false; 1979 } 1980 1981 public boolean dispatchContextItemSelected(MenuItem item) { 1982 if (mAdded != null) { 1983 for (int i=0; i<mAdded.size(); i++) { 1984 Fragment f = mAdded.get(i); 1985 if (f != null) { 1986 if (f.performContextItemSelected(item)) { 1987 return true; 1988 } 1989 } 1990 } 1991 } 1992 return false; 1993 } 1994 1995 public void dispatchOptionsMenuClosed(Menu menu) { 1996 if (mAdded != null) { 1997 for (int i=0; i<mAdded.size(); i++) { 1998 Fragment f = mAdded.get(i); 1999 if (f != null) { 2000 f.performOptionsMenuClosed(menu); 2001 } 2002 } 2003 } 2004 } 2005 2006 @Override 2007 public void invalidateOptionsMenu() { 2008 if (mActivity != null && mCurState == Fragment.RESUMED) { 2009 mActivity.invalidateOptionsMenu(); 2010 } else { 2011 mNeedMenuInvalidate = true; 2012 } 2013 } 2014 2015 public static int reverseTransit(int transit) { 2016 int rev = 0; 2017 switch (transit) { 2018 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 2019 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; 2020 break; 2021 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 2022 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; 2023 break; 2024 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 2025 rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE; 2026 break; 2027 } 2028 return rev; 2029 2030 } 2031 2032 public static int transitToStyleIndex(int transit, boolean enter) { 2033 int animAttr = -1; 2034 switch (transit) { 2035 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 2036 animAttr = enter 2037 ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation 2038 : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation; 2039 break; 2040 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 2041 animAttr = enter 2042 ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation 2043 : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation; 2044 break; 2045 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 2046 animAttr = enter 2047 ? com.android.internal.R.styleable.FragmentAnimation_fragmentFadeEnterAnimation 2048 : com.android.internal.R.styleable.FragmentAnimation_fragmentFadeExitAnimation; 2049 break; 2050 } 2051 return animAttr; 2052 } 2053 } 2054