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