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