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