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