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