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