FragmentManager.java revision 681a6fb06bdedb8661a68a1b9e34727b6059aa39
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.res.Configuration; 20import android.os.Bundle; 21import android.os.Handler; 22import android.os.Looper; 23import android.os.Parcel; 24import android.os.Parcelable; 25import android.support.v4.util.DebugUtils; 26import android.support.v4.util.LogWriter; 27import android.util.Log; 28import android.util.SparseArray; 29import android.view.animation.Animation; 30import android.view.animation.AnimationUtils; 31import android.view.animation.Animation.AnimationListener; 32import android.view.Menu; 33import android.view.MenuInflater; 34import android.view.MenuItem; 35import android.view.View; 36import android.view.ViewGroup; 37 38import java.io.FileDescriptor; 39import java.io.PrintWriter; 40import java.util.ArrayList; 41import java.util.Arrays; 42 43/** 44 * Static library support version of the framework's {@link android.app.FragmentManager}. 45 * Used to write apps that run on platforms prior to Android 3.0. When running 46 * on Android 3.0 or above, this implementation is still used; it does not try 47 * to switch to the framework's implementation. See the framework SDK 48 * documentation for a class overview. 49 * 50 * <p>Your activity must derive from {@link FragmentActivity} to use this. 51 */ 52public abstract class FragmentManager { 53 /** 54 * Representation of an entry on the fragment back stack, as created 55 * with {@link FragmentTransaction#addToBackStack(String) 56 * FragmentTransaction.addToBackStack()}. Entries can later be 57 * retrieved with {@link FragmentManager#getBackStackEntryAt(int) 58 * FragmentManager.getBackStackEntry()}. 59 * 60 * <p>Note that you should never hold on to a BackStackEntry object; 61 * the identifier as returned by {@link #getId} is the only thing that 62 * will be persisted across activity instances. 63 */ 64 public interface BackStackEntry { 65 /** 66 * Return the unique identifier for the entry. This is the only 67 * representation of the entry that will persist across activity 68 * instances. 69 */ 70 public int getId(); 71 72 /** 73 * Return the full bread crumb title resource identifier for the entry, 74 * or 0 if it does not have one. 75 */ 76 public int getBreadCrumbTitleRes(); 77 78 /** 79 * Return the short bread crumb title resource identifier for the entry, 80 * or 0 if it does not have one. 81 */ 82 public int getBreadCrumbShortTitleRes(); 83 84 /** 85 * Return the full bread crumb title for the entry, or null if it 86 * does not have one. 87 */ 88 public CharSequence getBreadCrumbTitle(); 89 90 /** 91 * Return the short bread crumb title for the entry, or null if it 92 * does not have one. 93 */ 94 public CharSequence getBreadCrumbShortTitle(); 95 } 96 97 /** 98 * Interface to watch for changes to the back stack. 99 */ 100 public interface OnBackStackChangedListener { 101 /** 102 * Called whenever the contents of the back stack change. 103 */ 104 public void onBackStackChanged(); 105 } 106 107 /** 108 * Start a series of edit operations on the Fragments associated with 109 * this FragmentManager. 110 * 111 * <p>Note: A fragment transaction can only be created/committed prior 112 * to an activity saving its state. If you try to commit a transaction 113 * after {@link FragmentActivity#onSaveInstanceState FragmentActivity.onSaveInstanceState()} 114 * (and prior to a following {@link FragmentActivity#onStart FragmentActivity.onStart} 115 * or {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will get an error. 116 * This is because the framework takes care of saving your current fragments 117 * in the state, and if changes are made after the state is saved then they 118 * will be lost.</p> 119 */ 120 public abstract FragmentTransaction beginTransaction(); 121 122 /** @hide -- remove once prebuilts are in. */ 123 @Deprecated 124 public FragmentTransaction openTransaction() { 125 return beginTransaction(); 126 } 127 128 /** 129 * After a {@link FragmentTransaction} is committed with 130 * {@link FragmentTransaction#commit FragmentTransaction.commit()}, it 131 * is scheduled to be executed asynchronously on the process's main thread. 132 * If you want to immediately executing any such pending operations, you 133 * can call this function (only from the main thread) to do so. Note that 134 * all callbacks and other related behavior will be done from within this 135 * call, so be careful about where this is called from. 136 * 137 * @return Returns true if there were any pending transactions to be 138 * executed. 139 */ 140 public abstract boolean executePendingTransactions(); 141 142 /** 143 * Finds a fragment that was identified by the given id either when inflated 144 * from XML or as the container ID when added in a transaction. This first 145 * searches through fragments that are currently added to the manager's 146 * activity; if no such fragment is found, then all fragments currently 147 * on the back stack associated with this ID are searched. 148 * @return The fragment if found or null otherwise. 149 */ 150 public abstract Fragment findFragmentById(int id); 151 152 /** 153 * Finds a fragment that was identified by the given tag either when inflated 154 * from XML or as supplied when added in a transaction. This first 155 * searches through fragments that are currently added to the manager's 156 * activity; if no such fragment is found, then all fragments currently 157 * on the back stack are searched. 158 * @return The fragment if found or null otherwise. 159 */ 160 public abstract Fragment findFragmentByTag(String tag); 161 162 /** 163 * Flag for {@link #popBackStack(String, int)} 164 * and {@link #popBackStack(int, int)}: If set, and the name or ID of 165 * a back stack entry has been supplied, then all matching entries will 166 * be consumed until one that doesn't match is found or the bottom of 167 * the stack is reached. Otherwise, all entries up to but not including that entry 168 * will be removed. 169 */ 170 public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; 171 172 /** 173 * Pop the top state off the back stack. Returns true if there was one 174 * to pop, else false. This function is asynchronous -- it enqueues the 175 * request to pop, but the action will not be performed until the application 176 * returns to its event loop. 177 */ 178 public abstract void popBackStack(); 179 180 /** 181 * Like {@link #popBackStack()}, but performs the operation immediately 182 * inside of the call. This is like calling {@link #executePendingTransactions()} 183 * afterwards. 184 * @return Returns true if there was something popped, else false. 185 */ 186 public abstract boolean popBackStackImmediate(); 187 188 /** 189 * Pop the last fragment transition from the manager's fragment 190 * back stack. If there is nothing to pop, false is returned. 191 * This function is asynchronous -- it enqueues the 192 * request to pop, but the action will not be performed until the application 193 * returns to its event loop. 194 * 195 * @param name If non-null, this is the name of a previous back state 196 * to look for; if found, all states up to that state will be popped. The 197 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 198 * the named state itself is popped. If null, only the top state is popped. 199 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 200 */ 201 public abstract void popBackStack(String name, int flags); 202 203 /** 204 * Like {@link #popBackStack(String, int)}, but performs the operation immediately 205 * inside of the call. This is like calling {@link #executePendingTransactions()} 206 * afterwards. 207 * @return Returns true if there was something popped, else false. 208 */ 209 public abstract boolean popBackStackImmediate(String name, int flags); 210 211 /** 212 * Pop all back stack states up to the one with the given identifier. 213 * This function is asynchronous -- it enqueues the 214 * request to pop, but the action will not be performed until the application 215 * returns to its event loop. 216 * 217 * @param id Identifier of the stated to be popped. If no identifier exists, 218 * false is returned. 219 * The identifier is the number returned by 220 * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. The 221 * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether 222 * the named state itself is popped. 223 * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}. 224 */ 225 public abstract void popBackStack(int id, int flags); 226 227 /** 228 * Like {@link #popBackStack(int, 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(int id, int flags); 234 235 /** 236 * Return the number of entries currently in the back stack. 237 */ 238 public abstract int getBackStackEntryCount(); 239 240 /** 241 * Return the BackStackEntry at index <var>index</var> in the back stack; 242 * entries start index 0 being the bottom of the stack. 243 */ 244 public abstract BackStackEntry getBackStackEntryAt(int index); 245 246 /** 247 * Add a new listener for changes to the fragment back stack. 248 */ 249 public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener); 250 251 /** 252 * Remove a listener that was previously added with 253 * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}. 254 */ 255 public abstract void removeOnBackStackChangedListener(OnBackStackChangedListener listener); 256 257 /** 258 * Put a reference to a fragment in a Bundle. This Bundle can be 259 * persisted as saved state, and when later restoring 260 * {@link #getFragment(Bundle, String)} will return the current 261 * instance of the same fragment. 262 * 263 * @param bundle The bundle in which to put the fragment reference. 264 * @param key The name of the entry in the bundle. 265 * @param fragment The Fragment whose reference is to be stored. 266 */ 267 public abstract void putFragment(Bundle bundle, String key, Fragment fragment); 268 269 /** 270 * Retrieve the current Fragment instance for a reference previously 271 * placed with {@link #putFragment(Bundle, String, Fragment)}. 272 * 273 * @param bundle The bundle from which to retrieve the fragment reference. 274 * @param key The name of the entry in the bundle. 275 * @return Returns the current Fragment instance that is associated with 276 * the given reference. 277 */ 278 public abstract Fragment getFragment(Bundle bundle, String key); 279 280 /** 281 * Print the FragmentManager's state into the given stream. 282 * 283 * @param prefix Text to print at the front of each line. 284 * @param fd The raw file descriptor that the dump is being sent to. 285 * @param writer A PrintWriter to which the dump is to be set. 286 * @param args Additional arguments to the dump request. 287 */ 288 public abstract void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args); 289 290 /** 291 * Control whether the framework's internal fragment manager debugging 292 * logs are turned on. If enabled, you will see output in logcat as 293 * the framework performs fragment operations. 294 */ 295 public static void enableDebugLogging(boolean enabled) { 296 FragmentManagerImpl.DEBUG = enabled; 297 } 298} 299 300final class FragmentManagerState implements Parcelable { 301 FragmentState[] mActive; 302 int[] mAdded; 303 BackStackState[] mBackStack; 304 305 public FragmentManagerState() { 306 } 307 308 public FragmentManagerState(Parcel in) { 309 mActive = in.createTypedArray(FragmentState.CREATOR); 310 mAdded = in.createIntArray(); 311 mBackStack = in.createTypedArray(BackStackState.CREATOR); 312 } 313 314 public int describeContents() { 315 return 0; 316 } 317 318 public void writeToParcel(Parcel dest, int flags) { 319 dest.writeTypedArray(mActive, flags); 320 dest.writeIntArray(mAdded); 321 dest.writeTypedArray(mBackStack, flags); 322 } 323 324 public static final Parcelable.Creator<FragmentManagerState> CREATOR 325 = new Parcelable.Creator<FragmentManagerState>() { 326 public FragmentManagerState createFromParcel(Parcel in) { 327 return new FragmentManagerState(in); 328 } 329 330 public FragmentManagerState[] newArray(int size) { 331 return new FragmentManagerState[size]; 332 } 333 }; 334} 335 336/** 337 * Container for fragments associated with an activity. 338 */ 339final class FragmentManagerImpl extends FragmentManager { 340 static boolean DEBUG = false; 341 static final String TAG = "FragmentManager"; 342 343 static final boolean HONEYCOMB = android.os.Build.VERSION.SDK_INT >= 11; 344 345 static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state"; 346 static final String TARGET_STATE_TAG = "android:target_state"; 347 static final String VIEW_STATE_TAG = "android:view_state"; 348 349 ArrayList<Runnable> mPendingActions; 350 Runnable[] mTmpActions; 351 boolean mExecutingActions; 352 353 ArrayList<Fragment> mActive; 354 ArrayList<Fragment> mAdded; 355 ArrayList<Integer> mAvailIndices; 356 ArrayList<BackStackRecord> mBackStack; 357 ArrayList<Fragment> mCreatedMenus; 358 359 // Must be accessed while locked. 360 ArrayList<BackStackRecord> mBackStackIndices; 361 ArrayList<Integer> mAvailBackStackIndices; 362 363 ArrayList<OnBackStackChangedListener> mBackStackChangeListeners; 364 365 int mCurState = Fragment.INITIALIZING; 366 FragmentActivity mActivity; 367 368 boolean mNeedMenuInvalidate; 369 boolean mStateSaved; 370 boolean mDestroyed; 371 String mNoTransactionsBecause; 372 373 // Temporary vars for state save and restore. 374 Bundle mStateBundle = null; 375 SparseArray<Parcelable> mStateArray = null; 376 377 Runnable mExecCommit = new Runnable() { 378 @Override 379 public void run() { 380 execPendingActions(); 381 } 382 }; 383 384 @Override 385 public FragmentTransaction beginTransaction() { 386 return new BackStackRecord(this); 387 } 388 389 @Override 390 public boolean executePendingTransactions() { 391 return execPendingActions(); 392 } 393 394 @Override 395 public void popBackStack() { 396 enqueueAction(new Runnable() { 397 @Override public void run() { 398 popBackStackState(mActivity.mHandler, null, -1, 0); 399 } 400 }, false); 401 } 402 403 @Override 404 public boolean popBackStackImmediate() { 405 checkStateLoss(); 406 executePendingTransactions(); 407 return popBackStackState(mActivity.mHandler, null, -1, 0); 408 } 409 410 @Override 411 public void popBackStack(final String name, final int flags) { 412 enqueueAction(new Runnable() { 413 @Override public void run() { 414 popBackStackState(mActivity.mHandler, name, -1, flags); 415 } 416 }, false); 417 } 418 419 @Override 420 public boolean popBackStackImmediate(String name, int flags) { 421 checkStateLoss(); 422 executePendingTransactions(); 423 return popBackStackState(mActivity.mHandler, name, -1, flags); 424 } 425 426 @Override 427 public void popBackStack(final int id, final int flags) { 428 if (id < 0) { 429 throw new IllegalArgumentException("Bad id: " + id); 430 } 431 enqueueAction(new Runnable() { 432 @Override public void run() { 433 popBackStackState(mActivity.mHandler, null, id, flags); 434 } 435 }, false); 436 } 437 438 @Override 439 public boolean popBackStackImmediate(int id, int flags) { 440 checkStateLoss(); 441 executePendingTransactions(); 442 if (id < 0) { 443 throw new IllegalArgumentException("Bad id: " + id); 444 } 445 return popBackStackState(mActivity.mHandler, null, id, flags); 446 } 447 448 @Override 449 public int getBackStackEntryCount() { 450 return mBackStack != null ? mBackStack.size() : 0; 451 } 452 453 @Override 454 public BackStackEntry getBackStackEntryAt(int index) { 455 return mBackStack.get(index); 456 } 457 458 @Override 459 public void addOnBackStackChangedListener(OnBackStackChangedListener listener) { 460 if (mBackStackChangeListeners == null) { 461 mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>(); 462 } 463 mBackStackChangeListeners.add(listener); 464 } 465 466 @Override 467 public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) { 468 if (mBackStackChangeListeners != null) { 469 mBackStackChangeListeners.remove(listener); 470 } 471 } 472 473 @Override 474 public void putFragment(Bundle bundle, String key, Fragment fragment) { 475 if (fragment.mIndex < 0) { 476 throw new IllegalStateException("Fragment " + fragment 477 + " is not currently in the FragmentManager"); 478 } 479 bundle.putInt(key, fragment.mIndex); 480 } 481 482 @Override 483 public Fragment getFragment(Bundle bundle, String key) { 484 int index = bundle.getInt(key, -1); 485 if (index == -1) { 486 return null; 487 } 488 if (index >= mActive.size()) { 489 throw new IllegalStateException("Fragement no longer exists for key " 490 + key + ": index " + index); 491 } 492 Fragment f = mActive.get(index); 493 if (f == null) { 494 throw new IllegalStateException("Fragement no longer exists for key " 495 + key + ": index " + index); 496 } 497 return f; 498 } 499 500 @Override 501 public String toString() { 502 StringBuilder sb = new StringBuilder(128); 503 sb.append("FragmentManager{"); 504 sb.append(Integer.toHexString(System.identityHashCode(this))); 505 sb.append(" in "); 506 DebugUtils.buildShortClassTag(mActivity, sb); 507 sb.append("}}"); 508 return sb.toString(); 509 } 510 511 @Override 512 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 513 String innerPrefix = prefix + " "; 514 515 int N; 516 if (mActive != null) { 517 N = mActive.size(); 518 if (N > 0) { 519 writer.print(prefix); writer.print("Active Fragments in "); 520 writer.print(Integer.toHexString(System.identityHashCode(this))); 521 writer.println(":"); 522 for (int i=0; i<N; i++) { 523 Fragment f = mActive.get(i); 524 writer.print(prefix); writer.print(" #"); writer.print(i); 525 writer.print(": "); writer.println(f); 526 if (f != null) { 527 f.dump(innerPrefix, fd, writer, args); 528 } 529 } 530 } 531 } 532 533 if (mAdded != null) { 534 N = mAdded.size(); 535 if (N > 0) { 536 writer.print(prefix); writer.println("Added Fragments:"); 537 for (int i=0; i<N; i++) { 538 Fragment f = mAdded.get(i); 539 writer.print(prefix); writer.print(" #"); writer.print(i); 540 writer.print(": "); writer.println(f.toString()); 541 } 542 } 543 } 544 545 if (mCreatedMenus != null) { 546 N = mCreatedMenus.size(); 547 if (N > 0) { 548 writer.print(prefix); writer.println("Fragments Created Menus:"); 549 for (int i=0; i<N; i++) { 550 Fragment f = mCreatedMenus.get(i); 551 writer.print(prefix); writer.print(" #"); writer.print(i); 552 writer.print(": "); writer.println(f.toString()); 553 } 554 } 555 } 556 557 if (mBackStack != null) { 558 N = mBackStack.size(); 559 if (N > 0) { 560 writer.print(prefix); writer.println("Back Stack:"); 561 for (int i=0; i<N; i++) { 562 BackStackRecord bs = mBackStack.get(i); 563 writer.print(prefix); writer.print(" #"); writer.print(i); 564 writer.print(": "); writer.println(bs.toString()); 565 bs.dump(innerPrefix, fd, writer, args); 566 } 567 } 568 } 569 570 synchronized (this) { 571 if (mBackStackIndices != null) { 572 N = mBackStackIndices.size(); 573 if (N > 0) { 574 writer.print(prefix); writer.println("Back Stack Indices:"); 575 for (int i=0; i<N; i++) { 576 BackStackRecord bs = mBackStackIndices.get(i); 577 writer.print(prefix); writer.print(" #"); writer.print(i); 578 writer.print(": "); writer.println(bs); 579 } 580 } 581 } 582 583 if (mAvailBackStackIndices != null && mAvailBackStackIndices.size() > 0) { 584 writer.print(prefix); writer.print("mAvailBackStackIndices: "); 585 writer.println(Arrays.toString(mAvailBackStackIndices.toArray())); 586 } 587 } 588 589 if (mPendingActions != null) { 590 N = mPendingActions.size(); 591 if (N > 0) { 592 writer.print(prefix); writer.println("Pending Actions:"); 593 for (int i=0; i<N; i++) { 594 Runnable r = mPendingActions.get(i); 595 writer.print(prefix); writer.print(" #"); writer.print(i); 596 writer.print(": "); writer.println(r); 597 } 598 } 599 } 600 601 writer.print(prefix); writer.println("FragmentManager misc state:"); 602 writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState); 603 writer.print(" mStateSaved="); writer.print(mStateSaved); 604 writer.print(" mDestroyed="); writer.println(mDestroyed); 605 if (mNeedMenuInvalidate) { 606 writer.print(prefix); writer.print(" mNeedMenuInvalidate="); 607 writer.println(mNeedMenuInvalidate); 608 } 609 if (mNoTransactionsBecause != null) { 610 writer.print(prefix); writer.print(" mNoTransactionsBecause="); 611 writer.println(mNoTransactionsBecause); 612 } 613 if (mAvailIndices != null && mAvailIndices.size() > 0) { 614 writer.print(prefix); writer.print(" mAvailIndices: "); 615 writer.println(Arrays.toString(mAvailIndices.toArray())); 616 } 617 } 618 619 Animation loadAnimator(Fragment fragment, int transit, boolean enter, 620 int transitionStyle) { 621 Animation animObj = fragment.onCreateAnimator(transit, enter, 622 fragment.mNextAnim); 623 if (animObj != null) { 624 return animObj; 625 } 626 627 if (fragment.mNextAnim != 0) { 628 Animation anim = AnimationUtils.loadAnimation(mActivity, fragment.mNextAnim); 629 if (anim != null) { 630 return anim; 631 } 632 } 633 634 if (transit == 0) { 635 return null; 636 } 637 638 int styleIndex = transitToStyleIndex(transit, enter); 639 if (styleIndex < 0) { 640 return null; 641 } 642 643 if (transitionStyle == 0 && mActivity.getWindow() != null) { 644 transitionStyle = mActivity.getWindow().getAttributes().windowAnimations; 645 } 646 if (transitionStyle == 0) { 647 return null; 648 } 649 650 //TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle, 651 // com.android.internal.R.styleable.FragmentAnimation); 652 //int anim = attrs.getResourceId(styleIndex, 0); 653 //attrs.recycle(); 654 655 //if (anim == 0) { 656 // return null; 657 //} 658 659 //return AnimatorInflater.loadAnimator(mActivity, anim); 660 return null; 661 } 662 663 void moveToState(Fragment f, int newState, int transit, int transitionStyle) { 664 // Fragments that are not currently added will sit in the onCreate() state. 665 if (!f.mAdded && newState > Fragment.CREATED) { 666 newState = Fragment.CREATED; 667 } 668 669 if (f.mState < newState) { 670 if (f.mAnimatingAway != null) { 671 // The fragment is currently being animated... but! Now we 672 // want to move our state back up. Give up on waiting for the 673 // animation, move to whatever the final state should be once 674 // the animation is done, and then we can proceed from there. 675 f.mAnimatingAway = null; 676 moveToState(f, f.mStateAfterAnimating, 0, 0); 677 } 678 switch (f.mState) { 679 case Fragment.INITIALIZING: 680 if (DEBUG) Log.v(TAG, "moveto CREATED: " + f); 681 if (f.mSavedFragmentState != null) { 682 f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray( 683 FragmentManagerImpl.VIEW_STATE_TAG); 684 f.mTarget = getFragment(f.mSavedFragmentState, 685 FragmentManagerImpl.TARGET_STATE_TAG); 686 if (f.mTarget != null) { 687 f.mTargetRequestCode = f.mSavedFragmentState.getInt( 688 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0); 689 } 690 } 691 f.mActivity = mActivity; 692 f.mFragmentManager = mActivity.mFragments; 693 f.mCalled = false; 694 f.onAttach(mActivity); 695 if (!f.mCalled) { 696 throw new SuperNotCalledException("Fragment " + f 697 + " did not call through to super.onAttach()"); 698 } 699 mActivity.onAttachFragment(f); 700 701 if (!f.mRetaining) { 702 f.mCalled = false; 703 f.onCreate(f.mSavedFragmentState); 704 if (!f.mCalled) { 705 throw new SuperNotCalledException("Fragment " + f 706 + " did not call through to super.onCreate()"); 707 } 708 } 709 f.mRetaining = false; 710 if (f.mFromLayout) { 711 // For fragments that are part of the content view 712 // layout, we need to instantiate the view immediately 713 // and the inflater will take care of adding it. 714 f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState), 715 null, f.mSavedFragmentState); 716 if (f.mView != null) { 717 f.mInnerView = f.mView; 718 f.mView = NoSaveStateFrameLayout.wrap(f.mView); 719 f.restoreViewState(); 720 if (f.mHidden) f.mView.setVisibility(View.GONE); 721 } else { 722 f.mInnerView = null; 723 } 724 } 725 case Fragment.CREATED: 726 if (newState > Fragment.CREATED) { 727 if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f); 728 if (!f.mFromLayout) { 729 ViewGroup container = null; 730 if (f.mContainerId != 0) { 731 container = (ViewGroup)mActivity.findViewById(f.mContainerId); 732 if (container == null && !f.mRestored) { 733 throw new IllegalArgumentException("No view found for id 0x" 734 + Integer.toHexString(f.mContainerId) 735 + " for fragment " + f); 736 } 737 } 738 f.mContainer = container; 739 f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState), 740 container, f.mSavedFragmentState); 741 if (f.mView != null) { 742 f.mInnerView = f.mView; 743 f.mView = NoSaveStateFrameLayout.wrap(f.mView); 744 if (container != null) { 745 Animation anim = loadAnimator(f, transit, true, 746 transitionStyle); 747 if (anim != null) { 748 f.mView.startAnimation(anim); 749 } 750 container.addView(f.mView); 751 f.restoreViewState(); 752 } 753 if (f.mHidden) f.mView.setVisibility(View.GONE); 754 } else { 755 f.mInnerView = null; 756 } 757 } 758 759 f.mCalled = false; 760 f.onActivityCreated(f.mSavedFragmentState); 761 if (!f.mCalled) { 762 throw new SuperNotCalledException("Fragment " + f 763 + " did not call through to super.onActivityCreated()"); 764 } 765 f.mSavedFragmentState = null; 766 } 767 case Fragment.ACTIVITY_CREATED: 768 if (newState > Fragment.ACTIVITY_CREATED) { 769 if (DEBUG) Log.v(TAG, "moveto STARTED: " + f); 770 f.mCalled = false; 771 f.onStart(); 772 if (!f.mCalled) { 773 throw new SuperNotCalledException("Fragment " + f 774 + " did not call through to super.onStart()"); 775 } 776 } 777 case Fragment.STARTED: 778 if (newState > Fragment.STARTED) { 779 if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); 780 f.mCalled = false; 781 f.mResumed = true; 782 f.onResume(); 783 if (!f.mCalled) { 784 throw new SuperNotCalledException("Fragment " + f 785 + " did not call through to super.onResume()"); 786 } 787 } 788 } 789 } else if (f.mState > newState) { 790 switch (f.mState) { 791 case Fragment.RESUMED: 792 if (newState < Fragment.RESUMED) { 793 if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); 794 f.mCalled = false; 795 f.onPause(); 796 if (!f.mCalled) { 797 throw new SuperNotCalledException("Fragment " + f 798 + " did not call through to super.onPause()"); 799 } 800 f.mResumed = false; 801 } 802 case Fragment.STARTED: 803 if (newState < Fragment.STARTED) { 804 if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f); 805 f.mCalled = false; 806 f.performStop(); 807 if (!f.mCalled) { 808 throw new SuperNotCalledException("Fragment " + f 809 + " did not call through to super.onStop()"); 810 } 811 } 812 case Fragment.ACTIVITY_CREATED: 813 if (newState < Fragment.ACTIVITY_CREATED) { 814 if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f); 815 if (f.mView != null) { 816 // Need to save the current view state if not 817 // done already. 818 if (!mActivity.isFinishing() && f.mSavedViewState == null) { 819 saveFragmentViewState(f); 820 } 821 } 822 f.mCalled = false; 823 f.onDestroyView(); 824 if (!f.mCalled) { 825 throw new SuperNotCalledException("Fragment " + f 826 + " did not call through to super.onDestroyView()"); 827 } 828 if (f.mView != null && f.mContainer != null) { 829 Animation anim = null; 830 if (mCurState > Fragment.INITIALIZING && !mDestroyed) { 831 anim = loadAnimator(f, transit, false, 832 transitionStyle); 833 } 834 if (anim != null) { 835 final Fragment fragment = f; 836 f.mAnimatingAway = f.mView; 837 f.mStateAfterAnimating = newState; 838 anim.setAnimationListener(new AnimationListener() { 839 @Override 840 public void onAnimationEnd(Animation animation) { 841 if (fragment.mAnimatingAway != null) { 842 fragment.mAnimatingAway = null; 843 moveToState(fragment, fragment.mStateAfterAnimating, 844 0, 0); 845 } 846 } 847 @Override 848 public void onAnimationRepeat(Animation animation) { 849 } 850 @Override 851 public void onAnimationStart(Animation animation) { 852 } 853 }); 854 f.mContainer.startAnimation(anim); 855 } 856 f.mContainer.removeView(f.mView); 857 } 858 f.mContainer = null; 859 f.mView = null; 860 f.mInnerView = null; 861 } 862 case Fragment.CREATED: 863 if (newState < Fragment.CREATED) { 864 if (mDestroyed) { 865 if (f.mAnimatingAway != null) { 866 // The fragment's containing activity is 867 // being destroyed, but this fragment is 868 // currently animating away. Stop the 869 // animation right now -- it is not needed, 870 // and we can't wait any more on destroying 871 // the fragment. 872 View v = f.mAnimatingAway; 873 f.mAnimatingAway = null; 874 v.clearAnimation(); 875 } 876 } 877 if (f.mAnimatingAway != null) { 878 // We are waiting for the fragment's view to finish 879 // animating away. Just make a note of the state 880 // the fragment now should move to once the animation 881 // is done. 882 f.mStateAfterAnimating = newState; 883 } else { 884 if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); 885 if (!f.mRetaining) { 886 f.mCalled = false; 887 f.onDestroy(); 888 if (!f.mCalled) { 889 throw new SuperNotCalledException("Fragment " + f 890 + " did not call through to super.onDestroy()"); 891 } 892 } 893 894 f.mCalled = false; 895 f.onDetach(); 896 if (!f.mCalled) { 897 throw new SuperNotCalledException("Fragment " + f 898 + " did not call through to super.onDetach()"); 899 } 900 f.mImmediateActivity = null; 901 f.mActivity = null; 902 f.mFragmentManager = null; 903 } 904 } 905 } 906 } 907 908 f.mState = newState; 909 } 910 911 void moveToState(Fragment f) { 912 moveToState(f, mCurState, 0, 0); 913 } 914 915 void moveToState(int newState, boolean always) { 916 moveToState(newState, 0, 0, always); 917 } 918 919 void moveToState(int newState, int transit, int transitStyle, boolean always) { 920 if (mActivity == null && newState != Fragment.INITIALIZING) { 921 throw new IllegalStateException("No activity"); 922 } 923 924 if (!always && mCurState == newState) { 925 return; 926 } 927 928 mCurState = newState; 929 if (mActive != null) { 930 for (int i=0; i<mActive.size(); i++) { 931 Fragment f = mActive.get(i); 932 if (f != null) { 933 moveToState(f, newState, transit, transitStyle); 934 } 935 } 936 937 if (mNeedMenuInvalidate && mActivity != null) { 938 mActivity.supportInvalidateOptionsMenu(); 939 mNeedMenuInvalidate = false; 940 } 941 } 942 } 943 944 void makeActive(Fragment f) { 945 if (f.mIndex >= 0) { 946 return; 947 } 948 949 if (mAvailIndices == null || mAvailIndices.size() <= 0) { 950 if (mActive == null) { 951 mActive = new ArrayList<Fragment>(); 952 } 953 f.setIndex(mActive.size()); 954 mActive.add(f); 955 956 } else { 957 f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1)); 958 mActive.set(f.mIndex, f); 959 } 960 } 961 962 void makeInactive(Fragment f) { 963 if (f.mIndex < 0) { 964 return; 965 } 966 967 if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex); 968 mActive.set(f.mIndex, null); 969 if (mAvailIndices == null) { 970 mAvailIndices = new ArrayList<Integer>(); 971 } 972 mAvailIndices.add(f.mIndex); 973 mActivity.invalidateFragmentIndex(f.mIndex); 974 f.clearIndex(); 975 } 976 977 public void addFragment(Fragment fragment, boolean moveToStateNow) { 978 if (mAdded == null) { 979 mAdded = new ArrayList<Fragment>(); 980 } 981 mAdded.add(fragment); 982 makeActive(fragment); 983 if (DEBUG) Log.v(TAG, "add: " + fragment); 984 fragment.mAdded = true; 985 fragment.mRemoving = false; 986 if (fragment.mHasMenu) { 987 mNeedMenuInvalidate = true; 988 } 989 if (moveToStateNow) { 990 moveToState(fragment); 991 } 992 } 993 994 public void removeFragment(Fragment fragment, int transition, int transitionStyle) { 995 if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting); 996 mAdded.remove(fragment); 997 final boolean inactive = fragment.mBackStackNesting <= 0; 998 if (fragment.mHasMenu) { 999 mNeedMenuInvalidate = true; 1000 } 1001 fragment.mAdded = false; 1002 fragment.mRemoving = true; 1003 moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, 1004 transition, transitionStyle); 1005 if (inactive) { 1006 makeInactive(fragment); 1007 } 1008 } 1009 1010 public void hideFragment(Fragment fragment, int transition, int transitionStyle) { 1011 if (DEBUG) Log.v(TAG, "hide: " + fragment); 1012 if (!fragment.mHidden) { 1013 fragment.mHidden = true; 1014 if (fragment.mView != null) { 1015 Animation anim = loadAnimator(fragment, transition, true, 1016 transitionStyle); 1017 if (anim != null) { 1018 fragment.mView.startAnimation(anim); 1019 } 1020 fragment.mView.setVisibility(View.GONE); 1021 } 1022 if (fragment.mAdded && fragment.mHasMenu) { 1023 mNeedMenuInvalidate = true; 1024 } 1025 fragment.onHiddenChanged(true); 1026 } 1027 } 1028 1029 public void showFragment(Fragment fragment, int transition, int transitionStyle) { 1030 if (DEBUG) Log.v(TAG, "show: " + fragment); 1031 if (fragment.mHidden) { 1032 fragment.mHidden = false; 1033 if (fragment.mView != null) { 1034 Animation anim = loadAnimator(fragment, transition, true, 1035 transitionStyle); 1036 if (anim != null) { 1037 fragment.mView.startAnimation(anim); 1038 } 1039 fragment.mView.setVisibility(View.VISIBLE); 1040 } 1041 if (fragment.mAdded && fragment.mHasMenu) { 1042 mNeedMenuInvalidate = true; 1043 } 1044 fragment.onHiddenChanged(false); 1045 } 1046 } 1047 1048 public Fragment findFragmentById(int id) { 1049 if (mActive != null) { 1050 // First look through added fragments. 1051 for (int i=mAdded.size()-1; i>=0; i--) { 1052 Fragment f = mAdded.get(i); 1053 if (f != null && f.mFragmentId == id) { 1054 return f; 1055 } 1056 } 1057 // Now for any known fragment. 1058 for (int i=mActive.size()-1; i>=0; i--) { 1059 Fragment f = mActive.get(i); 1060 if (f != null && f.mFragmentId == id) { 1061 return f; 1062 } 1063 } 1064 } 1065 return null; 1066 } 1067 1068 public Fragment findFragmentByTag(String tag) { 1069 if (mActive != null && tag != null) { 1070 // First look through added fragments. 1071 for (int i=mAdded.size()-1; i>=0; i--) { 1072 Fragment f = mAdded.get(i); 1073 if (f != null && tag.equals(f.mTag)) { 1074 return f; 1075 } 1076 } 1077 // Now for any known fragment. 1078 for (int i=mActive.size()-1; i>=0; i--) { 1079 Fragment f = mActive.get(i); 1080 if (f != null && tag.equals(f.mTag)) { 1081 return f; 1082 } 1083 } 1084 } 1085 return null; 1086 } 1087 1088 public Fragment findFragmentByWho(String who) { 1089 if (mActive != null && who != null) { 1090 for (int i=mActive.size()-1; i>=0; i--) { 1091 Fragment f = mActive.get(i); 1092 if (f != null && who.equals(f.mWho)) { 1093 return f; 1094 } 1095 } 1096 } 1097 return null; 1098 } 1099 1100 private void checkStateLoss() { 1101 if (mStateSaved) { 1102 throw new IllegalStateException( 1103 "Can not perform this action after onSaveInstanceState"); 1104 } 1105 if (mNoTransactionsBecause != null) { 1106 throw new IllegalStateException( 1107 "Can not perform this action inside of " + mNoTransactionsBecause); 1108 } 1109 } 1110 1111 public void enqueueAction(Runnable action, boolean allowStateLoss) { 1112 if (!allowStateLoss) { 1113 checkStateLoss(); 1114 } 1115 synchronized (this) { 1116 if (mActivity == null) { 1117 throw new IllegalStateException("Activity has been destroyed"); 1118 } 1119 if (mPendingActions == null) { 1120 mPendingActions = new ArrayList<Runnable>(); 1121 } 1122 mPendingActions.add(action); 1123 if (mPendingActions.size() == 1) { 1124 mActivity.mHandler.removeCallbacks(mExecCommit); 1125 mActivity.mHandler.post(mExecCommit); 1126 } 1127 } 1128 } 1129 1130 public int allocBackStackIndex(BackStackRecord bse) { 1131 synchronized (this) { 1132 if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) { 1133 if (mBackStackIndices == null) { 1134 mBackStackIndices = new ArrayList<BackStackRecord>(); 1135 } 1136 int index = mBackStackIndices.size(); 1137 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1138 mBackStackIndices.add(bse); 1139 return index; 1140 1141 } else { 1142 int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); 1143 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1144 mBackStackIndices.set(index, bse); 1145 return index; 1146 } 1147 } 1148 } 1149 1150 public void setBackStackIndex(int index, BackStackRecord bse) { 1151 synchronized (this) { 1152 if (mBackStackIndices == null) { 1153 mBackStackIndices = new ArrayList<BackStackRecord>(); 1154 } 1155 int N = mBackStackIndices.size(); 1156 if (index < N) { 1157 if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); 1158 mBackStackIndices.set(index, bse); 1159 } else { 1160 while (N < index) { 1161 mBackStackIndices.add(null); 1162 if (mAvailBackStackIndices == null) { 1163 mAvailBackStackIndices = new ArrayList<Integer>(); 1164 } 1165 if (DEBUG) Log.v(TAG, "Adding available back stack index " + N); 1166 mAvailBackStackIndices.add(N); 1167 N++; 1168 } 1169 if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); 1170 mBackStackIndices.add(bse); 1171 } 1172 } 1173 } 1174 1175 public void freeBackStackIndex(int index) { 1176 synchronized (this) { 1177 mBackStackIndices.set(index, null); 1178 if (mAvailBackStackIndices == null) { 1179 mAvailBackStackIndices = new ArrayList<Integer>(); 1180 } 1181 if (DEBUG) Log.v(TAG, "Freeing back stack index " + index); 1182 mAvailBackStackIndices.add(index); 1183 } 1184 } 1185 1186 /** 1187 * Only call from main thread! 1188 */ 1189 public boolean execPendingActions() { 1190 if (mExecutingActions) { 1191 throw new IllegalStateException("Recursive entry to executePendingTransactions"); 1192 } 1193 1194 if (Looper.myLooper() != mActivity.mHandler.getLooper()) { 1195 throw new IllegalStateException("Must be called from main thread of process"); 1196 } 1197 1198 boolean didSomething = false; 1199 1200 while (true) { 1201 int numActions; 1202 1203 synchronized (this) { 1204 if (mPendingActions == null || mPendingActions.size() == 0) { 1205 return didSomething; 1206 } 1207 1208 numActions = mPendingActions.size(); 1209 if (mTmpActions == null || mTmpActions.length < numActions) { 1210 mTmpActions = new Runnable[numActions]; 1211 } 1212 mPendingActions.toArray(mTmpActions); 1213 mPendingActions.clear(); 1214 mActivity.mHandler.removeCallbacks(mExecCommit); 1215 } 1216 1217 mExecutingActions = true; 1218 for (int i=0; i<numActions; i++) { 1219 mTmpActions[i].run(); 1220 } 1221 mExecutingActions = false; 1222 didSomething = true; 1223 } 1224 } 1225 1226 void reportBackStackChanged() { 1227 if (mBackStackChangeListeners != null) { 1228 for (int i=0; i<mBackStackChangeListeners.size(); i++) { 1229 mBackStackChangeListeners.get(i).onBackStackChanged(); 1230 } 1231 } 1232 } 1233 1234 void addBackStackState(BackStackRecord state) { 1235 if (mBackStack == null) { 1236 mBackStack = new ArrayList<BackStackRecord>(); 1237 } 1238 mBackStack.add(state); 1239 reportBackStackChanged(); 1240 } 1241 1242 boolean popBackStackState(Handler handler, String name, int id, int flags) { 1243 if (mBackStack == null) { 1244 return false; 1245 } 1246 if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) { 1247 int last = mBackStack.size()-1; 1248 if (last < 0) { 1249 return false; 1250 } 1251 final BackStackRecord bss = mBackStack.remove(last); 1252 bss.popFromBackStack(true); 1253 reportBackStackChanged(); 1254 } else { 1255 int index = -1; 1256 if (name != null || id >= 0) { 1257 // If a name or ID is specified, look for that place in 1258 // the stack. 1259 index = mBackStack.size()-1; 1260 while (index >= 0) { 1261 BackStackRecord bss = mBackStack.get(index); 1262 if (name != null && name.equals(bss.getName())) { 1263 break; 1264 } 1265 if (id >= 0 && id == bss.mIndex) { 1266 break; 1267 } 1268 index--; 1269 } 1270 if (index < 0) { 1271 return false; 1272 } 1273 if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) { 1274 index--; 1275 // Consume all following entries that match. 1276 while (index >= 0) { 1277 BackStackRecord bss = mBackStack.get(index); 1278 if ((name != null && name.equals(bss.getName())) 1279 || (id >= 0 && id == bss.mIndex)) { 1280 index--; 1281 continue; 1282 } 1283 break; 1284 } 1285 } 1286 } 1287 if (index == mBackStack.size()-1) { 1288 return false; 1289 } 1290 final ArrayList<BackStackRecord> states 1291 = new ArrayList<BackStackRecord>(); 1292 for (int i=mBackStack.size()-1; i>index; i--) { 1293 states.add(mBackStack.remove(i)); 1294 } 1295 final int LAST = states.size()-1; 1296 for (int i=0; i<=LAST; i++) { 1297 if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i)); 1298 states.get(i).popFromBackStack(i == LAST); 1299 } 1300 reportBackStackChanged(); 1301 } 1302 return true; 1303 } 1304 1305 ArrayList<Fragment> retainNonConfig() { 1306 ArrayList<Fragment> fragments = null; 1307 if (mActive != null) { 1308 for (int i=0; i<mActive.size(); i++) { 1309 Fragment f = mActive.get(i); 1310 if (f != null && f.mRetainInstance) { 1311 if (fragments == null) { 1312 fragments = new ArrayList<Fragment>(); 1313 } 1314 fragments.add(f); 1315 f.mRetaining = true; 1316 } 1317 } 1318 } 1319 return fragments; 1320 } 1321 1322 void saveFragmentViewState(Fragment f) { 1323 if (f.mInnerView == null) { 1324 return; 1325 } 1326 if (mStateArray == null) { 1327 mStateArray = new SparseArray<Parcelable>(); 1328 } 1329 f.mInnerView.saveHierarchyState(mStateArray); 1330 if (mStateArray.size() > 0) { 1331 f.mSavedViewState = mStateArray; 1332 mStateArray = null; 1333 } 1334 } 1335 1336 Parcelable saveAllState() { 1337 // Make sure all pending operations have now been executed to get 1338 // our state update-to-date. 1339 execPendingActions(); 1340 1341 if (HONEYCOMB) { 1342 // As of Honeycomb, we save state after pausing. Prior to that 1343 // it is before pausing. With fragments this is an issue, since 1344 // there are many things you may do after pausing but before 1345 // stopping that change the fragment state. For those older 1346 // devices, we will not at this point say that we have saved 1347 // the state, so we will allow them to continue doing fragment 1348 // transactions. This retains the same semantics as Honeycomb, 1349 // though you do have the risk of losing the very most recent state 1350 // if the process is killed... we'll live with that. 1351 mStateSaved = true; 1352 } 1353 1354 if (mActive == null || mActive.size() <= 0) { 1355 return null; 1356 } 1357 1358 // First collect all active fragments. 1359 int N = mActive.size(); 1360 FragmentState[] active = new FragmentState[N]; 1361 boolean haveFragments = false; 1362 for (int i=0; i<N; i++) { 1363 Fragment f = mActive.get(i); 1364 if (f != null) { 1365 haveFragments = true; 1366 1367 FragmentState fs = new FragmentState(f); 1368 active[i] = fs; 1369 1370 if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) { 1371 if (mStateBundle == null) { 1372 mStateBundle = new Bundle(); 1373 } 1374 f.onSaveInstanceState(mStateBundle); 1375 if (!mStateBundle.isEmpty()) { 1376 fs.mSavedFragmentState = mStateBundle; 1377 mStateBundle = null; 1378 } 1379 1380 if (f.mView != null) { 1381 saveFragmentViewState(f); 1382 if (f.mSavedViewState != null) { 1383 if (fs.mSavedFragmentState == null) { 1384 fs.mSavedFragmentState = new Bundle(); 1385 } 1386 fs.mSavedFragmentState.putSparseParcelableArray( 1387 FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState); 1388 } 1389 } 1390 1391 if (f.mTarget != null) { 1392 if (f.mTarget.mIndex < 0) { 1393 String msg = "Failure saving state: " + f 1394 + " has target not in fragment manager: " + f.mTarget; 1395 Log.e(TAG, msg); 1396 dump(" ", null, new PrintWriter(new LogWriter(TAG)), new String[] { }); 1397 throw new IllegalStateException(msg); 1398 } 1399 if (fs.mSavedFragmentState == null) { 1400 fs.mSavedFragmentState = new Bundle(); 1401 } 1402 putFragment(fs.mSavedFragmentState, 1403 FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget); 1404 if (f.mTargetRequestCode != 0) { 1405 fs.mSavedFragmentState.putInt( 1406 FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 1407 f.mTargetRequestCode); 1408 } 1409 } 1410 1411 } else { 1412 fs.mSavedFragmentState = f.mSavedFragmentState; 1413 } 1414 1415 if (DEBUG) Log.v(TAG, "Saved state of " + f + ": " 1416 + fs.mSavedFragmentState); 1417 } 1418 } 1419 1420 if (!haveFragments) { 1421 if (DEBUG) Log.v(TAG, "saveAllState: no fragments!"); 1422 return null; 1423 } 1424 1425 int[] added = null; 1426 BackStackState[] backStack = null; 1427 1428 // Build list of currently added fragments. 1429 if (mAdded != null) { 1430 N = mAdded.size(); 1431 if (N > 0) { 1432 added = new int[N]; 1433 for (int i=0; i<N; i++) { 1434 added[i] = mAdded.get(i).mIndex; 1435 if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i 1436 + ": " + mAdded.get(i)); 1437 } 1438 } 1439 } 1440 1441 // Now save back stack. 1442 if (mBackStack != null) { 1443 N = mBackStack.size(); 1444 if (N > 0) { 1445 backStack = new BackStackState[N]; 1446 for (int i=0; i<N; i++) { 1447 backStack[i] = new BackStackState(this, mBackStack.get(i)); 1448 if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i 1449 + ": " + mBackStack.get(i)); 1450 } 1451 } 1452 } 1453 1454 FragmentManagerState fms = new FragmentManagerState(); 1455 fms.mActive = active; 1456 fms.mAdded = added; 1457 fms.mBackStack = backStack; 1458 return fms; 1459 } 1460 1461 void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) { 1462 // If there is no saved state at all, then there can not be 1463 // any nonConfig fragments either, so that is that. 1464 if (state == null) return; 1465 FragmentManagerState fms = (FragmentManagerState)state; 1466 if (fms.mActive == null) return; 1467 1468 // First re-attach any non-config instances we are retaining back 1469 // to their saved state, so we don't try to instantiate them again. 1470 if (nonConfig != null) { 1471 for (int i=0; i<nonConfig.size(); i++) { 1472 Fragment f = nonConfig.get(i); 1473 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f); 1474 FragmentState fs = fms.mActive[f.mIndex]; 1475 fs.mInstance = f; 1476 f.mSavedViewState = null; 1477 f.mBackStackNesting = 0; 1478 f.mInLayout = false; 1479 f.mAdded = false; 1480 if (fs.mSavedFragmentState != null) { 1481 fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader()); 1482 f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( 1483 FragmentManagerImpl.VIEW_STATE_TAG); 1484 } 1485 } 1486 } 1487 1488 // Build the full list of active fragments, instantiating them from 1489 // their saved state. 1490 mActive = new ArrayList<Fragment>(fms.mActive.length); 1491 if (mAvailIndices != null) { 1492 mAvailIndices.clear(); 1493 } 1494 for (int i=0; i<fms.mActive.length; i++) { 1495 FragmentState fs = fms.mActive[i]; 1496 if (fs != null) { 1497 Fragment f = fs.instantiate(mActivity); 1498 if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": " + f); 1499 mActive.add(f); 1500 // Now that the fragment is instantiated (or came from being 1501 // retained above), clear mInstance in case we end up re-restoring 1502 // from this FragmentState again. 1503 fs.mInstance = null; 1504 } else { 1505 if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": (null)"); 1506 mActive.add(null); 1507 if (mAvailIndices == null) { 1508 mAvailIndices = new ArrayList<Integer>(); 1509 } 1510 if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i); 1511 mAvailIndices.add(i); 1512 } 1513 } 1514 1515 // Update the target of all retained fragments. 1516 if (nonConfig != null) { 1517 for (int i=0; i<nonConfig.size(); i++) { 1518 Fragment f = nonConfig.get(i); 1519 if (f.mTarget != null) { 1520 if (f.mTarget.mIndex < mActive.size()) { 1521 f.mTarget = mActive.get(f.mTarget.mIndex); 1522 } else { 1523 Log.w(TAG, "Re-attaching retained fragment " + f 1524 + " target no longer exists: " + f.mTarget); 1525 f.mTarget = null; 1526 } 1527 } 1528 } 1529 } 1530 1531 // Build the list of currently added fragments. 1532 if (fms.mAdded != null) { 1533 mAdded = new ArrayList<Fragment>(fms.mAdded.length); 1534 for (int i=0; i<fms.mAdded.length; i++) { 1535 Fragment f = mActive.get(fms.mAdded[i]); 1536 if (f == null) { 1537 throw new IllegalStateException( 1538 "No instantiated fragment for index #" + fms.mAdded[i]); 1539 } 1540 f.mAdded = true; 1541 f.mImmediateActivity = mActivity; 1542 if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f); 1543 mAdded.add(f); 1544 } 1545 } else { 1546 mAdded = null; 1547 } 1548 1549 // Build the back stack. 1550 if (fms.mBackStack != null) { 1551 mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length); 1552 for (int i=0; i<fms.mBackStack.length; i++) { 1553 BackStackRecord bse = fms.mBackStack[i].instantiate(this); 1554 if (DEBUG) Log.v(TAG, "restoreAllState: adding bse #" + i 1555 + " (index " + bse.mIndex + "): " + bse); 1556 mBackStack.add(bse); 1557 if (bse.mIndex >= 0) { 1558 setBackStackIndex(bse.mIndex, bse); 1559 } 1560 } 1561 } else { 1562 mBackStack = null; 1563 } 1564 } 1565 1566 public void attachActivity(FragmentActivity activity) { 1567 if (mActivity != null) throw new IllegalStateException(); 1568 mActivity = activity; 1569 } 1570 1571 public void noteStateNotSaved() { 1572 mStateSaved = false; 1573 } 1574 1575 public void dispatchCreate() { 1576 mStateSaved = false; 1577 moveToState(Fragment.CREATED, false); 1578 } 1579 1580 public void dispatchActivityCreated() { 1581 mStateSaved = false; 1582 moveToState(Fragment.ACTIVITY_CREATED, false); 1583 } 1584 1585 public void dispatchStart() { 1586 mStateSaved = false; 1587 moveToState(Fragment.STARTED, false); 1588 } 1589 1590 public void dispatchResume() { 1591 mStateSaved = false; 1592 moveToState(Fragment.RESUMED, false); 1593 } 1594 1595 public void dispatchPause() { 1596 moveToState(Fragment.STARTED, false); 1597 } 1598 1599 public void dispatchStop() { 1600 // See saveAllState() for the explanation of this. We do this for 1601 // all platform versions, to keep our behavior more consistent between 1602 // them. 1603 mStateSaved = true; 1604 1605 moveToState(Fragment.ACTIVITY_CREATED, false); 1606 } 1607 1608 public void dispatchReallyStop(boolean retaining) { 1609 if (mActive != null) { 1610 for (int i=0; i<mAdded.size(); i++) { 1611 Fragment f = mAdded.get(i); 1612 if (f != null) { 1613 f.performReallyStop(retaining); 1614 } 1615 } 1616 } 1617 } 1618 1619 public void dispatchDestroy() { 1620 mDestroyed = true; 1621 moveToState(Fragment.INITIALIZING, false); 1622 mActivity = null; 1623 } 1624 1625 public void dispatchConfigurationChanged(Configuration newConfig) { 1626 if (mActive != null) { 1627 for (int i=0; i<mAdded.size(); i++) { 1628 Fragment f = mAdded.get(i); 1629 if (f != null) { 1630 f.onConfigurationChanged(newConfig); 1631 } 1632 } 1633 } 1634 } 1635 1636 public void dispatchLowMemory() { 1637 if (mActive != null) { 1638 for (int i=0; i<mAdded.size(); i++) { 1639 Fragment f = mAdded.get(i); 1640 if (f != null) { 1641 f.onLowMemory(); 1642 } 1643 } 1644 } 1645 } 1646 1647 public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) { 1648 boolean show = false; 1649 ArrayList<Fragment> newMenus = null; 1650 if (mActive != null) { 1651 for (int i=0; i<mAdded.size(); i++) { 1652 Fragment f = mAdded.get(i); 1653 if (f != null && !f.mHidden && f.mHasMenu) { 1654 show = true; 1655 f.onCreateOptionsMenu(menu, inflater); 1656 if (newMenus == null) { 1657 newMenus = new ArrayList<Fragment>(); 1658 } 1659 newMenus.add(f); 1660 } 1661 } 1662 } 1663 1664 if (mCreatedMenus != null) { 1665 for (int i=0; i<mCreatedMenus.size(); i++) { 1666 Fragment f = mCreatedMenus.get(i); 1667 if (newMenus == null || !newMenus.contains(f)) { 1668 f.onDestroyOptionsMenu(); 1669 } 1670 } 1671 } 1672 1673 mCreatedMenus = newMenus; 1674 1675 return show; 1676 } 1677 1678 public boolean dispatchPrepareOptionsMenu(Menu menu) { 1679 boolean show = false; 1680 if (mActive != null) { 1681 for (int i=0; i<mAdded.size(); i++) { 1682 Fragment f = mAdded.get(i); 1683 if (f != null && !f.mHidden && f.mHasMenu) { 1684 show = true; 1685 f.onPrepareOptionsMenu(menu); 1686 } 1687 } 1688 } 1689 return show; 1690 } 1691 1692 public boolean dispatchOptionsItemSelected(MenuItem item) { 1693 if (mActive != null) { 1694 for (int i=0; i<mAdded.size(); i++) { 1695 Fragment f = mAdded.get(i); 1696 if (f != null && !f.mHidden && f.mHasMenu) { 1697 if (f.onOptionsItemSelected(item)) { 1698 return true; 1699 } 1700 } 1701 } 1702 } 1703 return false; 1704 } 1705 1706 public boolean dispatchContextItemSelected(MenuItem item) { 1707 if (mActive != null) { 1708 for (int i=0; i<mAdded.size(); i++) { 1709 Fragment f = mAdded.get(i); 1710 if (f != null && !f.mHidden) { 1711 if (f.onContextItemSelected(item)) { 1712 return true; 1713 } 1714 } 1715 } 1716 } 1717 return false; 1718 } 1719 1720 public void dispatchOptionsMenuClosed(Menu menu) { 1721 if (mActive != null) { 1722 for (int i=0; i<mAdded.size(); i++) { 1723 Fragment f = mAdded.get(i); 1724 if (f != null && !f.mHidden && f.mHasMenu) { 1725 f.onOptionsMenuClosed(menu); 1726 } 1727 } 1728 } 1729 } 1730 1731 public static int reverseTransit(int transit) { 1732 int rev = 0; 1733 switch (transit) { 1734 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 1735 rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE; 1736 break; 1737 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 1738 rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN; 1739 break; 1740 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 1741 rev = FragmentTransaction.TRANSIT_FRAGMENT_FADE; 1742 break; 1743 } 1744 return rev; 1745 1746 } 1747 1748 public static int transitToStyleIndex(int transit, boolean enter) { 1749 int animAttr = -1; 1750 switch (transit) { 1751 case FragmentTransaction.TRANSIT_FRAGMENT_OPEN: 1752 animAttr = enter 1753 ? 0 //com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation 1754 : 0; //com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation; 1755 break; 1756 case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE: 1757 animAttr = enter 1758 ? 0 //com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation 1759 : 0; //com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation; 1760 break; 1761 case FragmentTransaction.TRANSIT_FRAGMENT_FADE: 1762 animAttr = enter 1763 ? 0 //com.android.internal.R.styleable.FragmentAnimation_fragmentFadeEnterAnimation 1764 : 0; //com.android.internal.R.styleable.FragmentAnimation_fragmentFadeExitAnimation; 1765 break; 1766 } 1767 return animAttr; 1768 } 1769} 1770